Skip to content

Your First Move Module

The Aptos blockchain allows developers to write Turing complete smart contracts (called “modules”) with the secure-by-design Move language. Smart contracts enable users to send money with the blockchain, but also write arbitrary code, even games! It all starts with the Aptos CLI creating an account which will store the deployed (”published”) Move module.

This tutorial will help you understand Move Modules by guiding you through setting up a minimal Aptos environment, then how to compile, test, publish and interact with Move modules on the Aptos Blockchain. You will learn how to:

  1. Setup your environment, install the CLI
  2. Create a devnet account and fund it
  3. Compile and test a Move module
  4. Publish (or “deploy”) a Move module to the Aptos blockchain
  5. Interact with the module
  6. Keep building with Aptos (next steps)

Changes to the blockchain are called “transactions”, and they require an account to pay the network fee (”gas fee”). We will need to create an account with some APT to pay that fee and own the published contract. In order to do that, we will need to use the Aptos CLI.

  1. Install the Aptos CLI

    Install the Aptos CLI (if you haven’t already).

  2. Open a new terminal

    Open a new terminal window or tab.

  3. Verify the installation

    Run aptos --version to verify you have it installed.

    Terminal window
    aptos --version

    You should see a response like aptos 4.6.1.

  4. Create a project folder

    Create a new folder for this tutorial by running:

    Terminal window
    mkdir my-first-module
  5. Navigate to the project folder

    Run cd my-first-module to go into your new folder.

  6. Initialize your account

    Run aptos init and press ‘enter’ for each step of setup to create a test account on devnet.

    You should see a success message like this:

    Terminal window
    ---
    Aptos CLI is now set up for account 0x9ec1cfa30b885a5c9d595f32f3381ec16d208734913b587be9e210f60be9f9ba as profile default!
    {
    "Result": "Success"
    }

2. (Optional) Explore What You Just Did On-Chain

Section titled “2. (Optional) Explore What You Just Did On-Chain”
  1. Copy your account address

    Copy the address from the command line for your new account.

    The address looks like this 0x9ec1cfa30b885a5c9d595f32f3381ec16d208734913b587be9e210f60be9f9ba and you can find it in the line:

    Terminal window
    Aptos CLI is now set up for account 0x9ec1cfa30b885a5c9d595f32f3381ec16d208734913b587be9e210f60be9f9ba as profile default!
  2. Open the Aptos Explorer

    Go to the Aptos Explorer.

    This is the primary way to quickly check what is happening on devnet, testnet, or mainnet. We will use it later on to view our deployed contracts.

  3. Ensure you are on Devnet network.

    Look for “Devnet” in the top right corner, or switch networks by clicking the “Mainnet” dropdown and selecting Devnet

    Switching to Devnet network in Aptos Explorer

  4. Search for your account

    Paste your newly created address into the search bar.

  5. View the search results

    Wait for the results to appear, then click the top result.

  6. Check the transaction

    You should see your newly created account and a transaction with the faucet function, funding it with devnet tokens.

    Viewing Account in Aptos Explorer

  7. Verify your balance

    Click the “Coins” tab to see that you have 1 APT of the Aptos Coin. This will allow you to publish and interact with smart contracts on the aptos devnet.

3. Writing and Compiling Your First Module

Section titled “3. Writing and Compiling Your First Module”

Now that we have our environment set up and an account created, let’s write and compile our first Move module. Unlike Ethereum where contracts exist independently, Move ties everything to accounts - both modules and their resources. Let’s start with a simple example to understand the core concepts.

Move Blockchain Diagram

This diagram illustrates the relationship between module ownership, token ownership, and the Move blockchain state. It helps visualize how modules and resources are tied to accounts, emphasizing the unique aspects of Move’s design compared to other blockchain platforms.

Move modules are similar to smart contracts in other blockchains, with some key differences:

  • Resources: Unlike Solidity where state is stored in contract variables, Move uses “resources” - special data types that can only exist in one place at a time and are always tied to an account
  • Module-based: Rather than deploying entire contracts as independent units like in Solidity, Move code is organized into reusable modules that can share and handle resources across boundaries. Modules are more like standard library packages that can be published together or separately, offering finer-grained control over code organization.
  • Safety by design: Move’s type system and resource semantics help prevent common smart contract vulnerabilities

Our first module will be a simple message storage system that allows accounts to store and retrieve messages. Let’s create a new move project within our my-first-module folder:

  1. Initialize the project

    Initialize a new move project with aptos move init --name my_first_module

    This creates a project structure with a sources directory and a Move.toml file.

  2. Create the module file

    Create a new file sources/message.move with our module code:

    module my_first_module::message {
    use std::string;
    use std::signer;
    struct MessageHolder has key, store, drop {
    message: string::String,
    }
    public entry fun set_message(account: &signer, message: string::String) acquires MessageHolder {
    let account_addr = signer::address_of(account);
    if (exists<MessageHolder>(account_addr)) {
    move_from<MessageHolder>(account_addr);
    };
    move_to(account, MessageHolder { message });
    }
    public fun get_message(account_addr: address): string::String acquires MessageHolder {
    assert!(exists<MessageHolder>(account_addr), 0);
    let message_holder = borrow_global<MessageHolder>(account_addr);
    message_holder.message
    }
    }

    Let’s break down this module:

    • We define a MessageHolder resource type that can store a string message
    • set_message allows an account to store a message
    • get_message allows anyone to retrieve a stored message
    • The acquires keyword indicates which resources the functions need access to (MessageHolder, in this case)
    • move_to and move_from handle the storage of resources under accounts
  3. Compile the module

    Compile the Move module we just created with aptos move compile --named-addresses my_first_module=default

    You should see a message like this if it succeeded:

    Terminal window
    aptos move compile --named-addresses my_first_module=default
    Compiling, may take a little while to download git dependencies...
    UPDATING GIT DEPENDENCY https://github.com/aptos-labs/aptos-framework.git
    INCLUDING DEPENDENCY AptosFramework
    INCLUDING DEPENDENCY AptosStdlib
    INCLUDING DEPENDENCY MoveStdlib
    BUILDING my_first_module
    {
    "Result": [
    "9ec1cfa30b885a5c9d595f32f3381ec16d208734913b587be9e210f60be9f9ba::message"
    ]
    }

Great job! We are now ready to test and debug.

Testing and debugging are crucial parts of Move module development. Move has built-in support for unit testing and debug printing.

  1. Add debug prints

    First, let’s modify our message module to add some debug prints. Update your sources/message.move:

    module my_first_module::message {
    use std::string;
    use std::signer;
    use std::debug; // Add this for debug prints
    struct MessageHolder has key, store, drop {
    message: string::String,
    }
    public entry fun set_message(account: &signer, message: string::String) acquires MessageHolder {
    let account_addr = signer::address_of(account);
    debug::print(&message); // Print the message being set
    if (exists<MessageHolder>(account_addr)) {
    debug::print(&string::utf8(b"Updating existing message")); // Print debug info
    move_from<MessageHolder>(account_addr);
    } else {
    debug::print(&string::utf8(b"Creating new message")); // Print when creating new
    };
    move_to(account, MessageHolder { message });
    }
    public fun get_message(account_addr: address): string::String acquires MessageHolder {
    assert!(exists<MessageHolder>(account_addr), 0);
    let message_holder = borrow_global<MessageHolder>(account_addr);
    debug::print(&message_holder.message); // Print the retrieved message
    message_holder.message
    }
    }
  2. Create test file

    Create our tests: a new file sources/message_tests.move with:

    #[test_only]
    module my_first_module::message_tests {
    use std::string;
    use std::signer;
    use aptos_framework::account;
    use my_first_module::message;
    #[test]
    fun test_set_and_get_message() {
    // Set up test account
    let test_account = account::create_account_for_test(@0x1);
    // Test setting a message
    message::set_message(&test_account, string::utf8(b"Hello World"));
    // Verify the message was set correctly
    let stored_message = message::get_message(signer::address_of(&test_account));
    assert!(stored_message == string::utf8(b"Hello World"), 0);
    }
    }
  3. Run the tests

    Now run the tests with aptos move test --named-addresses my_first_module=default

    You should see output if the tests pass: (See below for how to handle errors)

    Terminal window
    INCLUDING DEPENDENCY AptosFramework
    INCLUDING DEPENDENCY AptosStdlib
    INCLUDING DEPENDENCY MoveStdlib
    BUILDING my_first_module
    Running Move unit tests
    [debug] "Hello World"
    [debug] "Creating new message"
    [debug] "Hello World"
    [ PASS ] 0x9ec1cfa30b885a5c9d595f32f3381ec16d208734913b587be9e210f60be9f9ba::message_tests::test_set_and_get_message
    Test result: OK. Total tests: 1; passed: 1; failed: 0
    {
    "Result": "Success"
    }

If you encounter errors while testing, here are some common issues and solutions:

  • Make sure all module dependencies are properly imported
  • Check that your account address matches in the -named-addresses parameter
  • Verify that test functions have the #[test] attribute
  • Ensure string literals are properly encoded

After successfully compiling and testing your module, you can publish it to the Aptos blockchain. This process deploys your code so that it’s accessible on-chain.

  1. Publish the module

    Publish your module with aptos move publish --named-addresses my_first_module=default

    You’ll see output showing the compilation process and then a prompt asking about gas fees:

    Terminal window
    Compiling, may take a little while to download git dependencies...
    UPDATING GIT DEPENDENCY https://github.com/aptos-labs/aptos-framework.git
    INCLUDING DEPENDENCY AptosFramework
    INCLUDING DEPENDENCY AptosStdlib
    INCLUDING DEPENDENCY MoveStdlib
    BUILDING my_first_module
    package size 1271 bytes
    Do you want to submit a transaction for a range of [141300 - 211900] Octas at a gas unit price of 100 Octas? [yes/no] >
  2. Confirm the transaction

    Type y and press Enter to confirm the transaction.

    After confirmation, you’ll receive a response showing the transaction details:

    Terminal window
    {
    "Result": {
    "transaction_hash": "0x95fce7344b066abda10c07dbf1ffa83e0d9c7bd400e2b143682a6c8a5f179dc2",
    "gas_used": 1413,
    "gas_unit_price": 100,
    "sender": "9ec1cfa30b885a5c9d595f32f3381ec16d208734913b587be9e210f60be9f9ba",
    "sequence_number": 0,
    "success": true,
    "timestamp_us": 1735351260227638,
    "version": 273029731,
    "vm_status": "Executed successfully"
    }
    }

After successful publication, you can verify your module is on-chain by following these steps:

  1. Open the Explorer

    Go to the Aptos Explorer

  2. Check the transaction

    Search for your account address. You should notice that there is a new transaction in your account, the code::publish_package_txn function.

  3. View your balance

    Click the “Coins” tab to see that you now have less than 1 APT of the Aptos Coin.

    Explorer Coins View

    You have spent a small amount on gas to deploy the contract so should have around 0.99855 APT remaining.

  4. Find your module

    Look under the “Modules” tab

    Exporer Modules View

  5. Verify the module

    You should see your “message” module listed

Now that your module is published, you can interact with it through the Aptos CLI:

  1. Set a message

    Set a message using the CLI:

    Terminal window
    aptos move run --function-id 'default::message::set_message' --args 'string:Hello, Aptos!'

    You’ll see a gas fee prompt similar to what you saw during publishing.

  2. Confirm the transaction

    After confirming with y, you should get a success response like:

    Terminal window
    Transaction submitted: https://explorer.aptoslabs.com/txn/0x0c0b1e56a31d037280278327eb8fdfcc469a20213e5e65accf6e7c56af574449?network=devnet
    {
    "Result": {
    "transaction_hash": "0x0c0b1e56a31d037280278327eb8fdfcc469a20213e5e65accf6e7c56af574449",
    "gas_used": 445,
    "gas_unit_price": 100,
    "sender": "9ec1cfa30b885a5c9d595f32f3381ec16d208734913b587be9e210f60be9f9ba",
    "sequence_number": 1,
    "success": true,
    "timestamp_us": 1735351754495208,
    "version": 273137362,
    "vm_status": "Executed successfully"
    }
    }
  3. View your message

    View your stored message by checking under Resources on the Explorer.

  4. Celebrate!

    We did it!

Congratulations! You’ve successfully:

  1. Compiled your first Move module
  2. Added tests to help debug
  3. Published your module on-chain
  4. Used your contract through the CLI

Now your published Move module can be connected to just like an API via one of our many Official SDKs!

Here are some suggested next steps to get a deeper understanding of Move modules:

  1. Try modifying the module to add a new feature. You can use the Move Book to build your understanding of writing Move modules.
  2. To understand how Move works on-chain, you can learn about Move’s resource system.
  3. If you’re building an application to interact with contracts or look up data from on-chain, learn how to use the SDKs here.
  4. Join the Aptos Discord to connect with other developers.