Set up the move toolchain

Move-based programming requires a specific compiler and command-line interface to translate your smart contract logic into bytecode. This setup guide walks you through installing the Rust toolchain, the Move CLI, and verifying that your environment is ready to compile. We will use the standard installation methods recommended for developers on macOS, Linux, and Windows.

move-based programming
1
Install Rust and Cargo

Move is compiled from Rust, so you need the Rust toolchain installed first. Open your terminal and run the official installer. This command downloads rustup, which manages your Rust version and associated tools like Cargo (the package manager).

Shell
Shell
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Follow the prompts to complete the installation. Once done, source your environment variables so the commands are available in your current shell session:

Shell
Shell
source "$HOME/.cargo/env"
move-based programming
2
Install the Move CLI

With Rust ready, you can now install the Move compiler and CLI tools. The move command-line interface is the primary tool for creating, compiling, and testing Move packages. Install it using Cargo:

Shell
Shell
cargo install --git https://github.com/MoveProgramming/move move-cli

This command fetches the latest stable version of the Move CLI from the official repository and builds it from source. This process may take a few minutes depending on your internet connection and system speed.

move-based programming
3
Verify the installation

Confirm that both Rust and the Move CLI are correctly installed by checking their versions. Run the following commands in your terminal:

Shell
Shell
rustc --version
move --version

You should see version numbers printed for both tools. If move is not recognized, ensure your Cargo bin directory is in your system PATH. A successful output confirms your Move-based programming environment is ready for development.

Write a basic object contract

Move treats assets as objects with unique identifiers rather than fungible balances attached to accounts. This object-centric model simplifies ownership logic by tying the asset’s state directly to its ID. We will define a minimal module that creates a simple digital item, assigns it an ID, and stores it in a container.

move-based programming
1
Set up the module skeleton

Every Move contract starts with a module declaration. This block defines the address where the contract lives and imports the standard library modules required for object management. We need object for the core type and transfer to handle movement.

MOVE
MOVE
module my_project::simple_item {
    use std::string;
    use sui::object::{Self, Object, UID};
    use sui::transfer;
    use sui::tx_context::{Self, TxContext};

    // Module logic goes here
}
move-based programming
2
Define the object struct

Move objects must include a UID field. This field is automatically managed by the Move VM and ensures that each instance of the object has a unique identifier on the blockchain. We also add a name field to store a simple string value.

MOVE
MOVE
struct SimpleItem has key {
    id: UID,
    name: string::String,
}
The to Move-Based Programming
3
Create a constructor function

The init function creates a new instance of the object. It uses object::new(ctx) to generate a fresh UID and initializes the name field. The function returns the object wrapped in a way that allows it to be transferred or stored.

MOVE
MOVE
public fun init(name: string, ctx: &mut TxContext) { 
    let item = SimpleItem {
        id: object::new(ctx),
        name: string::utf8(name),
    };
    // Return the object to the caller
    transfer::public_transfer(item, tx_context::sender(ctx));
}
The to Move-Based Programming
4
Transfer the object

Unlike traditional languages where objects are passed by reference, Move objects are moved by value. The transfer::public_transfer function moves the object from the contract’s context to the sender’s account. This explicit move ensures that only one account can own the object at any given time.

MOVE
MOVE
public fun transfer_item(item: SimpleItem, recipient: address, ctx: &mut TxContext) {
    transfer::public_transfer(item, recipient);
}

This basic contract demonstrates the core principle of Move-based programming: explicit ownership. By defining a struct with the key ability, we signal to the Move VM that this type can be stored in persistent storage or transferred between accounts. The UID field guarantees uniqueness, preventing duplicate assets from being created accidentally.

When you compile this module, the Move compiler checks that all fields are properly initialized and that the object is transferred correctly. This static analysis helps prevent common blockchain bugs like lost assets or double-spending before the code ever reaches the network.

Deploy to the Sui testnet

Deploying to the Sui testnet allows you to verify your Move smart contracts on a live blockchain without risking real value. The testnet mirrors mainnet behavior, including gas costs and transaction finality, making it the ideal staging ground before any production release.

Before deploying, ensure you have SUI tokens in your wallet to cover gas fees. You can obtain testnet tokens from the official Sui faucet by providing your wallet address. Without sufficient balance, your publish command will fail with an "insufficient balance" error.

1
Connect your wallet to testnet

Open your wallet extension or CLI configuration and switch the network profile to testnet. Verify the connection by checking your balance via the Sui Explorer. This step ensures your signer is targeting the correct blockchain environment.

2
Run the build command

Execute sui move build in your package directory. This command compiles your Move sources into bytecode and generates the package metadata. Ensure there are no compilation errors before proceeding, as the publish step requires a valid package artifact.

3
Publish the package

Run sui client publish --gas-budget 10000000. This command uploads your compiled bytecode to the testnet. The --gas-budget flag sets the maximum SUI you are willing to spend on the transaction. Monitor the output for the transaction digest.

4
Verify the deployment

Copy the transaction digest from the previous step and paste it into the Sui Explorer. Confirm that the package ID is listed and that the module is published. A successful deployment means your smart contract is now live on the testnet and ready for integration testing.

Fix common Move errors

Move’s ownership model is strict by design, which means the compiler will catch issues that other languages might let slide into production. When you hit a compilation error, it usually boils down to one of three things: you’re trying to use a value after it’s been consumed, you’re attempting to copy a resource that doesn’t support it, or you’re violating module visibility rules. Understanding these patterns will save you hours of debugging.

Handle moved values correctly

In Move, every value has exactly one owner. Once you pass a value to a function or assign it to a variable, the original location is invalid. If you try to use it again, the compiler throws a "use of moved value" error. This isn’t a bug; it’s the language preventing double-spending or data races at the type level.

To fix this, ensure you only access a value once. If you need to share data, use references (&T or &mut T) instead of moving the value itself. For example, if a function takes account: &mut Account, it borrows the account rather than taking ownership, allowing you to use the account variable afterward.

Manage resource copying

Resources are special structs that cannot be copied by default. If you define a struct with the key or store capability, you must explicitly mark it with copy if you want to duplicate it. Without the copy ability, attempting to clone a resource will result in a compilation error. This ensures that unique assets like NFTs or tokens aren’t accidentally duplicated.

If you need to copy a resource, add the copy ability to your struct definition: struct MyResource has key, store, copy { ... }. If you don’t need copying, stick to references or move semantics to maintain strict ownership. This distinction is critical for security; accidental copying can lead to significant vulnerabilities in smart contracts.

Resolve visibility and module errors

Move enforces strict module boundaries. If you try to access a function or struct from another module without the correct visibility modifier, the compiler will reject it. Functions marked as public are only callable within the same module. To allow external access, you must mark them as public(entry) for transaction entry points or public for general external calls.

Similarly, struct fields are private by default. To allow other modules to read or modify fields, you must expose getters or setters. If you’re getting "field not found" or "function not visible" errors, check your visibility modifiers. Ensure that the struct has the public ability if it needs to be stored on-chain, and that functions are correctly exposed.

Use the Move CLI for better diagnostics

The Move compiler provides detailed error messages, but they can be cryptic for newcomers. Use the move build command to get a clear overview of all errors in your project. The CLI will point to the exact line and file where the issue originates. Additionally, the move test command can help you isolate runtime errors in unit tests before deploying to mainnet.

If you’re stuck, consult the Move Book for official documentation on ownership and resource management. The Move language is designed to be safe, so if the compiler complains, it’s usually protecting you from a logical error. Take the time to understand the error message rather than bypassing it with workarounds.

Check your contract security

Before you deploy, you must verify that your Move code is secure. Move is designed for safety, but human error still happens. A single vulnerability can lead to lost funds or broken logic. You need a systematic way to catch these issues before they reach mainnet.

Start by running the built-in Move compiler and static analyzer. These tools catch common mistakes like type mismatches or unused resources. They are your first line of defense against basic errors.

Next, use formal verification tools if your contract handles high-value assets. Formal proofs mathematically prove your code behaves as intended. This step is optional for simple contracts but essential for complex financial logic.

Finally, run unit tests and integration tests. Test every edge case you can think of, including reentrancy attempts and permission checks. Move’s resource model makes some traditional exploits impossible, but logic errors remain a risk.

Use this checklist to ensure you haven't missed anything critical.

Move-based programming 2026 FAQs

As the landscape shifts, developers often wonder if investing time in Move is still relevant. The short answer is yes, but the context has changed. Learning Move in 2026 means focusing on safety and resource-oriented design rather than just syntax. This guide answers the most common questions about getting started and understanding current trends.