Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/mystenlabs/sui/llms.txt

Use this file to discover all available pages before exploring further.

Move packages are the fundamental unit of code organization on Sui. Understanding their structure is essential for building well-organized smart contracts.

Package Anatomy

A typical Move package has this structure:
my_package/
├── Move.toml          # Package manifest
├── sources/           # Move source files
│   ├── module1.move
│   └── module2.move
├── tests/             # Test files (optional)
│   └── module1_tests.move
└── examples/          # Example code (optional)
    └── usage.move

Move.toml Manifest

The Move.toml file defines package metadata, dependencies, and addresses.

Basic Structure

From the coin example in the Sui codebase:
[package]
name = "Coins"
version = "0.0.1"
edition = "2024.beta"

[dependencies]
Sui = { local = "../../../crates/sui-framework/packages/sui-framework" }

[addresses]
examples = "0x0"

Package Section

[package]
name = "my_package"        # Package name
version = "1.0.0"          # Semantic version
edition = "2024.beta"      # Move edition

Dependencies

Local dependencies

[dependencies]
Sui = { local = "../sui-framework" }
MyLib = { local = "../my_lib" }

Git dependencies

[dependencies]
Sui = { 
    git = "https://github.com/MystenLabs/sui.git", 
    subdir = "crates/sui-framework/packages/sui-framework",
    rev = "framework/mainnet" 
}

Address Aliases

Define named addresses for your package:
[addresses]
my_package = "0x0"  # Placeholder, assigned at publish
admin = "0x1234..."  # Fixed address
In your code:
module my_package::example {
    // my_package is resolved from Move.toml
}

Module Structure

Modules are the building blocks of Move packages.

Basic Module

module package_name::module_name {
    // Imports
    use sui::object::{Self, UID};
    use sui::transfer;

    // Constants
    const MAX_SUPPLY: u64 = 1000000;
    const ENotAuthorized: u64 = 0;

    // Structs
    public struct MyObject has key {
        id: UID,
        value: u64,
    }

    // Functions
    public fun create(value: u64, ctx: &mut TxContext): MyObject {
        MyObject {
            id: object::new(ctx),
            value,
        }
    }

    // Tests
    #[test]
    fun test_create() {
        // Test code
    }
}

Module Initializer

The init function runs once when the package is published:
module my_package::example {
    public struct AdminCap has key {
        id: UID,
    }

    // Called once at publish time
    fun init(ctx: &mut TxContext) {
        let admin_cap = AdminCap {
            id: object::new(ctx),
        };
        transfer::transfer(admin_cap, ctx.sender());
    }
}

Organizing Code

Single vs Multiple Modules

Single module - Simple packages:
sources/
└── nft.move
Multiple modules - Complex packages:
sources/
├── nft.move           # Main NFT logic
├── marketplace.move   # Trading functionality  
├── royalties.move     # Royalty management
└── utils.move         # Shared utilities

Module Visibility

Public modules

Expose functionality to other packages:
module my_package::public_api {
    public fun do_something() { /* ... */ }
}

Internal modules

Keep implementation details private:
module my_package::internal {
    // Only accessible within my_package
    public(package) fun helper_function() { /* ... */ }
}

Friend Declarations

Allow specific modules to access package-private functions:
module my_package::core {
    // Declare friends
    friend my_package::admin;
    friend my_package::marketplace;

    // Only friends can call this
    public(package) fun privileged_operation() {
        // ...
    }
}

module my_package::admin {
    use my_package::core;

    public fun admin_action() {
        core::privileged_operation(); // Allowed
    }
}

Code Organization Patterns

Pattern 1: Core + Extensions

sources/
├── core.move       # Core data structures
├── admin.move      # Admin functions
├── user.move       # User-facing functions
└── events.move     # Event definitions

Pattern 2: Feature-Based

sources/
├── token.move           # Token implementation
├── staking.move         # Staking logic
├── governance.move      # Governance
└── treasury.move        # Treasury management

Pattern 3: Layer-Based

sources/
├── storage.move    # Data layer
├── logic.move      # Business logic
└── api.move        # Public API

Real Example: Coin Package

From examples/move/coin:
coin/
├── Move.toml
└── sources/
    ├── my_coin.move              # Basic coin
    ├── fixed_supply.move         # Fixed supply coin
    ├── deflationary_supply.move  # Deflationary coin
    └── regcoin.move              # Regulated coin

my_coin.move

module examples::my_coin {
    use sui::coin::{Self, TreasuryCap};

    public struct MY_COIN has drop {}

    fun init(witness: MY_COIN, ctx: &mut TxContext) {
        let (treasury, metadata) = coin::create_currency(
            witness,
            6,              // decimals
            b"MY_COIN",     // symbol
            b"",            // name
            b"",            // description
            option::none(), // icon_url
            ctx,
        );
        transfer::public_freeze_object(metadata);
        transfer::public_transfer(treasury, ctx.sender())
    }

    public fun mint(
        treasury_cap: &mut TreasuryCap<MY_COIN>,
        amount: u64,
        recipient: address,
        ctx: &mut TxContext,
    ) {
        let coin = coin::mint(treasury_cap, amount, ctx);
        transfer::public_transfer(coin, recipient)
    }
}

Building Packages

Build command

sui move build

Build with docs

sui move build --doc

Check dependencies

sui move build --fetch-deps-only

Publishing Packages

Publish to network

sui client publish --gas-budget 100000000

Publish with custom addresses

Update Move.toml:
[addresses]
my_package = "0x<address>"
Then publish:
sui client publish --gas-budget 100000000

Package Upgrades

Sui supports package upgrades. See the Upgrade Packages guide for details.

Upgrade policies

  • Compatible: Only add new functions/structs
  • Additive: Can add abilities to structs
  • Incompatible: Breaking changes

Best Practices

1. One concern per module

Keep modules focused:
// Good
module my_package::user_profile { /* ... */ }
module my_package::user_badges { /* ... */ }

// Avoid
module my_package::everything { /* ... */ }

2. Use clear naming

module defi_protocol::liquidity_pool { /* ... */ }
module defi_protocol::swap_router { /* ... */ }

3. Organize imports

module my_package::example {
    // Standard library
    use std::string;
    use std::vector;

    // Sui framework
    use sui::object::{Self, UID};
    use sui::transfer;
    use sui::tx_context::TxContext;

    // Package imports
    use my_package::utils;
    use my_package::events;
}

4. Document public APIs

/// Create a new NFT with the given attributes.
/// 
/// # Arguments
/// * `name` - The NFT name
/// * `description` - The NFT description
/// * `url` - The image URL
public fun mint_nft(
    name: vector<u8>,
    description: vector<u8>,
    url: vector<u8>,
    ctx: &mut TxContext
): NFT {
    // Implementation
}

5. Version your packages

[package]
name = "my_package"
version = "1.2.0"  # Use semantic versioning

Next Steps

Build docs developers (and LLMs) love