Let's Move - Learn Move, Earn Sui: Deploying Coin Contracts on Mainnet

·

The Let's Move initiative is designed to empower developers by encouraging hands-on learning of the Move programming language, particularly within the Sui blockchain ecosystem. This guide walks you through Task 2: deploying two custom coin contracts — My Coin and Faucet Coin — onto the Sui mainnet. By mastering contract deployment, minting, and burning mechanisms, you'll gain practical experience with core Sui development workflows.

Whether you're building your first token or exploring asset management in Move, this step-by-step walkthrough ensures clarity, accuracy, and real-world applicability.


Understanding the Task Requirements

To complete this phase of the Let's Move challenge, participants must:

This task emphasizes secure, standards-compliant token creation using Sui’s built-in coin module — a critical skill for any aspiring Web3 developer.

👉 Discover how to accelerate your blockchain development journey with powerful tools


Core Concepts: The sui::coin Module

At the heart of every custom token on Sui lies the sui::coin library. It provides essential structures for creating and managing fungible tokens with full control over minting and burning.

The Coin<T> Structure

struct Coin<phantom T> has key, store {
    id: UID,
    balance: Balance<T>
}

This structure represents a user-owned token with:

Because it has the key ability, Coin<T> can be retrieved from global storage using its object ID. The store ability allows it to be transferred between accounts.

The Balance<T> Type

struct Balance<phantom T> has store {
    value: u64
}

This internal type holds the actual numeric value of the coin supply. It is wrapped inside the Coin struct and managed via safe APIs.


Creating a Custom Currency with create_currency

To launch a new token, we use the create_currency function:

public fun create_currency<T: drop>(
    witness: T,
    decimals: u8,
    symbol: vector<u8>,
    name: vector<u8>,
    description: vector<u8>,
    icon_url: Option<Url>,
    ctx: &mut TxContext
): (TreasuryCap<T>, CoinMetadata<T>)
ParameterPurpose
decimalsNumber of decimal places (e.g., 2 = divisible to 0.01)
symbolTicker symbol (e.g., "MC")
nameFull name of the token
descriptionHuman-readable description
icon_urlOptional URL for token logo

Return Values

🔐 The TreasuryCap follows the one-time witness pattern, ensuring only one instance exists per token type — preventing unauthorized duplication.

Building Your First Coin: My Coin

Below is a complete implementation of a deployable coin module using Move:

// File: my_coin.move
module new_coin::my_coin {
    use std::option;
    use sui::coin::{Self, Coin, TreasuryCap};
    use sui::transfer;
    use sui::tx_context::{Self, TxContext};

    struct MY_COIN has drop {}

    fun init(witness: MY_COIN, ctx: &mut TxContext) {
        let (treasury_cap, metadata) = coin::create_currency<MY_COIN>(
            witness,
            2,
            b"MY_COIN",
            b"MC",
            b"Learning for letsmove, powered by alva-lin",
            option::none(),
            ctx
        );
        transfer::public_freeze_object(metadata);
        transfer::public_transfer(treasury_cap, tx_context::sender(ctx));
    }

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

    public fun burn(
        treasury_cap: &mut TreasuryCap<MY_COIN>,
        coin: Coin<MY_COIN>
    ) {
        coin::burn(treasury_cap, coin);
    }

    #[test_only]
    public fun test_init(ctx: &mut TxContext) {
        init(MY_COIN {}, ctx);
    }
}

Key Design Decisions


Testing Minting and Burning Logic

A robust test suite simulates real-world usage:

#[test_only]
module new_coin::my_coin_tests {
    use new_coin::my_coin::{Self, MY_COIN};
    use sui::coin::{Coin, TreasuryCap};
    use sui::test_scenario::{Self, next_tx, ctx};

    #[test]
    fun mint_burn() {
        let addr1 = @0xA;
        let scenario = test_scenario::begin(addr1);

        // Initialize module
        my_coin::test_init(ctx(&mut scenario));

        // Mint 100 units (value = 100 * 10^2 = 10000)
        next_tx(&mut scenario, addr1);
        let treasurycap = test_scenario::take_from_sender<TreasuryCap<MY_COIN>>(&scenario);
        my_coin::mint(&mut treasurycap, 10000, addr1, test_scenario::ctx(&mut scenario));
        test_scenario::return_to_address<TreasuryCap<MY_COIN>>(addr1, treasurycap);

        // Burn the coin
        next_tx(&mut scenario, addr1);
        let coin = test_scenario::take_from_sender<Coin<MY_COIN>>(&scenario);
        let treasurycap = test_scenario::take_from_sender<TreasuryCap<MY_COIN>>(&scenario);
        my_coin::burn(&mut treasurycap, coin);
        test_scenario::return_to_address<TreasuryCap<MY_COIN>>(addr1, treasurycap);

        test_scenario::end(scenario);
    }
}

This test verifies that:


Deploying to Sui Mainnet

Before deployment, ensure your CLI is configured for production:

sui client switch --env mainnet

Then publish your package:

sui client publish --gas-budget 30000

Upon successful deployment, note these outputs:

Export them for later use:

export PACKAGE_ID=0xf9623587d620d26024868578a3539642993e2da44598c3fcaa1f384f5327f6a5
export TREASURYCAP_ID=0x97233be4acd1688c93f2c60ce81350b993a810c6b4851e8cdf214402759fad88

👉 Start experimenting with smart contracts using industry-leading platforms


Minting and Burning Tokens

Minting Process

Remember: final amount = input × 10⁻ᵈᵉᶜᶦᵐᵃˡˢ
With decimals = 2, to mint 100 coins → pass 10000.

export RECIPIENT_ADDRESS=<YOUR_WALLET_ADDRESS>
sui client call --gas-budget 3000 \
  --package $PACKAGE_ID \
  --module my_coin \
  --function mint \
  --args $TREASURYCAP_ID "10000" $RECIPIENT_ADDRESS

After execution, locate the newly created Coin object in:

Object Changes > Created Objects > Type: 0x2::coin::Coin<...>

Record its Object ID:

export COIN_ID=0x3460204ae7f9385df79dc963a17d8b11eb0fa7a699f7196fac80405e1777d36c

Burning Tokens

Use the same TreasuryCap to destroy tokens:

sui client call --gas-budget 7500000 \
  --package $PACKAGE_ID \
  --module my_coin \
  --function burn \
  --args $TREASURYCAP_ID $COIN_ID
📌 Once burned, the coin disappears from wallet balances and cannot be queried directly by ID. Its history remains traceable via transaction logs on explorers like SuiScan.

Frequently Asked Questions (FAQ)

Q: What is the purpose of the one-time witness pattern?

A: It ensures that only one instance of a specific token type can exist. This prevents accidental or malicious re-creation of currency definitions, maintaining integrity across the network.

Q: Why should I freeze CoinMetadata after creation?

A: Freezing guarantees immutability. Once deployed, details like name, symbol, or decimals cannot change — which builds trust among users and dApps interacting with your token.

Q: Can anyone mint tokens if they get hold of the TreasuryCap?

A: Yes. The holder of the TreasuryCap has full minting and burning authority. Always keep it secure — consider multi-sig wallets or hardware signing solutions for production environments.

Q: How do I verify my contract was successfully deployed?

A: Use a block explorer like SuiScan. Enter your Package ID to view all associated objects, functions, and transaction history linked to your deployment.

Q: Is it possible to upgrade a published Move package?

A: On Sui, packages can be made upgradeable during initial publication. However, once marked as immutable, no further changes are allowed — emphasizing security through transparency.

👉 Access advanced development resources and tools to streamline your workflow


Final Thoughts

Completing this task equips you with foundational skills in Move-based token engineering. You've learned how to:

These competencies open doors to deeper engagement with decentralized finance (DeFi), NFT systems, and custom asset logic on Sui.

By focusing on clean architecture and safe practices — such as freezing metadata and guarding TreasuryCaps — you're not just completing a challenge; you're building habits that define professional blockchain development.

Core Keywords: Move language, Sui blockchain, coin contract deployment, fungible tokens, TreasuryCap, mint and burn, smart contract testing, mainnet deployment