Sui Move Standard Library - Core Token Package (balance, coin, pay)

This article analyzes the three core components of Sui Move tokens: balance (value storage layer), coin (token operation layer), and pay (payment abstraction layer). Through principle diagrams and code examples, the design principles of the three-layer architecture are revealed.

1. Module Description

Modules Tiers Function Scenario
sui::balance Value storage layer (underlying storage) Manage the mapping relationship between addresses and token balances Balance query, token ownership verification
sui::coin Token operation layer (basic operation) Focus on the life cycle management of the token itself, including: token creation, destruction and metadata management Custom token issuance and token metadata maintenance
sui::pay Payment abstraction layer (high-level encapsulation) Provides compound operations for token payment, including: single or batch token splitting, merging, transfer, etc. Batch transfer, split account logic, airdrop etc.

The balance.move: https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/sui-framework/sources/balance.move

The Coin.move: https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/sui-framework/sources/coin.move

The Pay.move: https://github.com/MystenLabs/sui/blob/main/crates/sui-framework/packages/sui-framework/sources/pay.move

2. Method Description

2.1 sui::balance

Classification Function Name Parameter Functional Description
Balance Operations zero No parameters Create a balance object with a quantity of zero
value self: &Balance<T> Get the amount stored in the balance object
join self: &mut Balance<T>, balance: Balance<T> Merge two balance objects
split self: &mut Balance<T>, value: u64 Separate the specified number of sub-balance objects from the balance object
withdraw_all self: &mut Balance<T> Withdraw all balances, remaining balance is 0
destroy_zero balance: Balance<T> Destroys the balance object with zero quantity
Supply Operation create_supply _: T Create new supply for type T
supply_value supply: &Supply<T> Get the value of supply
increase_supply self: &mut Supply<T>, value: u64 Increases the supply and creates a corresponding amount of balance
decrease_supply self: &mut Supply<T>, balance: Balance<T> Destroy balances and reduce supply
destroy_supply self: Supply<T> Destroys supply, preventing further minting and burning

2.2 sui::coin

Classification Function Name Parameter Functional Description
Supply Operation total_supply cap: &TreasuryCap<T> Returns the total number of tokens in circulation
treasury_into_supply treasury: TreasuryCap<T> Unpack TreasuryCap to obtain Supply (irreversible)
supply_immut treasury: &TreasuryCap<T> Get an immutable reference to the treasury’s Supply
supply_mut treasury: &mut TreasuryCap<T> Get a mutable reference to the treasury’s Supply
Balance Operations value self: &Coin<T> Get the value of the coin
balance coin: &Coin<T> Get an immutable reference to the coin’s balance
balance_mut coin: &mut Coin<T> Get a mutable reference to the coin’s balance
from_balance balance: Balance<T>, ctx: &mut TxContext Wrap the balance into transferable coins
into_balance coin: Coin<T> Deconstruct coin wrapper and preserve balance
take balance: &mut Balance<T>, value: u64, ctx: &mut TxContext Withdraw coins of specified value from balance
put balance: &mut Balance<T>, coin: Coin<T> Add coins to balance
Token Operations join self: &mut Coin<T>, c1: Coin<T> Merge two coins
split self: &mut Coin<T>, split_amount: u64, ctx: &mut TxContext Split the coin into two parts
divide_into_n self: &mut Coin<T>, n: u64, ctx: &mut TxContext Divide coin equally into n–1 coins
zero ctx: &mut TxContext Create a coin with zero value
destroy_zero c: Coin<T> Destroy coins with zero value
mint cap: &mut TreasuryCap<T>, value: u64, ctx: &mut TxContext Mint coins of specified value
mint_balance cap: &mut TreasuryCap<T>, value: u64 Mint a balance of specified value
burn cap: &mut TreasuryCap<T>, c: Coin<T> Destroy coins and reduce total supply
create_currency witness: T, decimals: u8, symbol: vector<u8>, name: vector<u8>, description: vector<u8>, icon_url: Option<u8>, ctx: &mut TxContext Create a new currency type
mint_and_transfer c: &mut TreasuryCap<T>, amount: u64, recipient: address, ctx: &mut TxContext Mint coins and transfer to recipients
Metadata Operations update_name t_treasury: &mut TreasuryCap<T>, metadata: &mut CoinMetadata<T>, name: string::String Update the name of the coin
update_symbol t_treasury: &mut TreasuryCap<T>, metadata: &mut CoinMetadata<T>, symbol: ascii::String Update coin symbol
update_description t_treasury: &mut TreasuryCap<T>, metadata: &mut CoinMetadata<T>, description: string::String Update coin description
update_icon_url t_treasury: &mut TreasuryCap<T>, metadata: &mut CoinMetadata<T>, url: ascii::String Update the coin icon URL
get_decimals metadata: &CoinMetadata<T> Get number of decimal places of the coin
get_name metadata: &CoinMetadata<T> Get the name of the coin
get_symbol metadata: &CoinMetadata<T> Get the coin symbol
get_description metadata: &CoinMetadata<T> Get the description of the coin
get_icon_url metadata: &CoinMetadata<T> Get the coin icon URL

2.3 sui::coin (Regulated currency & deny list functions)

Classification Function Name Parameter Functional Description
Create and Migrate create_regulated_currency_v2 witness: T, decimals: u8, symbol: vector<u8>, name: vector<u8>, description: vector<u8>, icon_url: Option<u8>, allow_global_pause: bool, ctx: &mut TxContext Creating a Currency with Regulatory Functions
migrate_regulated_currency_to_v2 deny_list: &mut DenyList, cap: DenyCap<T>, allow_global_pause: bool, ctx: &mut TxContext Migrate regulated currency to v2
Blacklist Operation deny_list_v2_add deny_list: &mut DenyList, _deny_cap: &mut DenyCapV2<T>, addr: address, ctx: &mut TxContext Add an address to a deny list
deny_list_v2_remove deny_list: &mut DenyList, _deny_cap: &mut DenyCapV2<T>, addr: address, ctx: &mut TxContext Remove an address from the deny list
deny_list_v2_contains_current_epoch deny_list: &DenyList, addr: address, ctx: &TxContext Check if current epoch's deny list contains the address
deny_list_v2_contains_next_epoch deny_list: &DenyList, addr: address Check if next epoch's deny list contains the address
deny_list_v2_enable_global_pause deny_list: &mut DenyList, deny_cap: &mut DenyCapV2<T>, ctx: &mut TxContext Enable global pause
deny_list_v2_disable_global_pause deny_list: &mut DenyList, deny_cap: &mut DenyCapV2<T>, ctx: &mut TxContext Disable global pause
deny_list_v2_is_global_pause_enabled_current_epoch deny_list: &DenyList, ctx: &TxContext Check if global pause is enabled for the current epoch
deny_list_v2_is_global_pause_enabled_next_epoch deny_list: &DenyList Check if global pause is enabled for the next epoch

2.4 sui::pay

Classification Function Name Parameter Functional Description
Transfer keep c: Coin<T>, ctx: &TxContext Transfer coins to the sender of the current transaction
Segmentation split coin: &mut Coin<T>, split_amount: u64, ctx: &mut TxContext Split the coin into two parts, specify the split amount
split_vec self: &mut Coin<T>, split_amounts: vector<u64>, ctx: &mut TxContext Split the coin into multiple coins, balances specified by a vector
split_and_transfer c: &mut Coin<T>, amount: u64, recipient: address, ctx: &mut TxContext Split specified amount of coins and send them to the recipient
divide_and_keep self: &mut Coin<T>, n: u64, ctx: &mut TxContext Divide the coin into n–1 coins equally, and keep the remainder
Merge join self: &mut Coin<T>, coin: Coin<T> Merge two coins (deprecated)
join_vec self: &mut Coin<T>, coins: vector<Coin<T>> Merge all coins in the vector into the main coin
join_vec_and_transfer coins: vector<Coin<T>>, receiver: address Combine the coins in the vector and transfer them to the recipient

3. Example

module cookbook::aig_token {
    use std::string::{Self, String};
    use std::ascii;
    use sui::coin::{Self, TreasuryCap};
    use sui::balance::{Self, Balance};
    use sui::url::{Self, Url};
    use sui::event;

    public struct EventMint has copy, drop {
        sender: address,
        amount: u64,
        coin_left: u64
    }

    public struct EventAirdrop has copy, drop {
        method: String,
        sender: address,
        amount: u64
    }

    public struct EventCoinMeta has copy, drop {
        decimals: u8,
        symbol: ascii::String,
        name: String,
        description: String,
        icon_url: Option<Url>,
    }

    public struct EventTotalSupply has copy, drop {
        total_supply: u64
    }

    public struct Vault has key {
        id: UID,
        balance: Balance<AIG_TOKEN>,
    }

    public struct AIG_TOKEN has drop {}

    fun init(
        witness: AIG_TOKEN,
        ctx: &mut TxContext
    ) {

        let decimals = 3;
        let symbol = b"AIG";
        let name = b"AIG Token";
        let description = b"AIG Token is a token that is used to incentivize the community to achieve the goals of the AI Goal.";
        let url = url::new_unsafe_from_bytes(b"https://ai-goal.vercel.app/");

        let (treasury_cap, metadata) = coin::create_currency<AIG_TOKEN>(
            witness,
            decimals,
            symbol,
            name,
            description,
            option::some(url),
            ctx
        );

        event::emit(
            EventCoinMeta {
                decimals: coin::get_decimals(&metadata),
                symbol: coin::get_symbol(&metadata),
                name: coin::get_name(&metadata),
                description: coin::get_description(&metadata),
                icon_url: option::some(url),
            }
        );

        transfer::public_freeze_object(metadata);
        transfer::public_transfer(treasury_cap, ctx.sender());

        transfer::share_object(
            Vault {
                id: object::new(ctx),
                balance: balance::zero(),
            }
        );
    }

    public(package) fun airdrop(
        vault: &mut Vault,
        amount: u64,
        method: vector<u8>,
        ctx: &mut TxContext
    ) {
        let sender = ctx.sender();

        let mut balance_drop = balance::split(&mut vault.balance, amount);
        let coin_drop = coin::take(&mut balance_drop, amount, ctx);
        transfer::public_transfer(coin_drop, sender);
        balance::destroy_zero(balance_drop);

        event::emit(
            EventAirdrop {
                method: string::utf8(method),
                sender,
                amount,
            }
        );
    }

    public fun mint_balance(
        treasury_cap: &mut TreasuryCap<AIG_TOKEN>,
        vault: &mut Vault,
        amount: u64,
        ctx: &mut TxContext
    ) {
        let balance_minted = coin::mint_balance(treasury_cap, amount);
        balance::join(&mut vault.balance, balance_minted);

        event::emit(
            EventMint {
                sender: ctx.sender(),
                amount: amount,
                coin_left: balance::value(&vault.balance)
            }
        );
    }

    #[allow(lint(self_transfer))]
    public fun mint_coin(
        treasury_cap: &mut TreasuryCap<AIG_TOKEN>,
        amount: u64,
        ctx: &mut TxContext
    ) {
        let coin_minted = coin::mint(treasury_cap, amount, ctx);
        transfer::public_transfer(coin_minted, ctx.sender());

        coin::mint_and_transfer(
            treasury_cap,
            amount,
            ctx.sender(),
            ctx
        );

        event::emit(
            EventTotalSupply {
                total_supply: coin::total_supply(treasury_cap)
            }
        )
    }
}