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)
}
)
}
}