Coins & Fungible Tokens
Recent Sui versions also introduce a Coin Registry (sui::coin_registry) for registering currency metadata in a central shared registry; create_currency remains widely used.
33.1 Sui: Coin, Balance, TreasuryCap
Owned object holding a balance; transferable Raw balance to embed inside other objects (pools, vaults)
Capability to mint and burn T; tracks total supply Name, symbol, decimals, description, icon Regulated coins: deny-list addresses
33.2 Creating a coin on Sui
Recent Sui versions also introduce a Coin Registry (sui::coin_registry) for registering currency metadata in a central shared registry; create_currency remains widely used. Regulated coins use coin::create_regulated_currency_v2 to obtain a DenyCapV2.
module my_coin::gold;
use sui::coin::{Self, TreasuryCap, Coin};
public struct GOLD has drop {}
fun init(otw: GOLD, ctx: &mut TxContext) {
let (treasury, metadata) = coin::create_currency(
otw,
9, // decimals
b"GOLD", // symbol
b"Gold Coin", // name
b"A course example coin", // description
option::none(), // icon url: Option<Url>
ctx,
);
transfer::public_freeze_object(metadata); // metadata immutable
transfer::public_transfer(treasury, ctx.sender()); // keep the mint authority
}
public fun mint(cap: &mut TreasuryCap<GOLD>, amount: u64, to: address, ctx: &mut TxContext) {
let c: Coin<GOLD> = coin::mint(cap, amount, ctx);
transfer::public_transfer(c, to);
}
public fun burn(cap: &mut TreasuryCap<GOLD>, c: Coin<GOLD>): u64 {
coin::burn(cap, c)
}33.3 Working with coins
SUI itself is Coin<sui::sui::SUI>, with 9 decimals (1 SUI = 10^9 MIST). Gas is paid in SUI.
Closed-loop tokens use sui::token::Token<T> with TokenPolicy rules (non-store, spending limited by policy) — useful for loyalty points and in-game currencies.
let mut c: Coin<SUI> = /* ... */;
let v = c.value();
let part: Coin<SUI> = c.split(100, ctx); // coin::split
c.join(part); // coin::join
let b: Balance<SUI> = c.into_balance(); // coin::into_balance
let c2 = coin::from_balance(b, ctx);
let z = coin::zero<SUI>(ctx); z.destroy_zero();
// balance ops: balance.split(n), balance.join(b), balance.value(), balance::zero()33.4 Aptos: Coin (legacy) and Fungible Asset (FA)
Aptos originally used 0x1::coin::Coin<CoinType> stored in CoinStore<CoinType> resources. The newer Fungible Asset standard (0x1::fungible_asset) built on objects is now the recommended standard; APT and existing coins are being migrated so that coin balances are represented as FA under the hood (paired FA metadata).
| | Coin standard | Fungible Asset standard | |-|---------------|-------------------------| | Type identity | Generic Coin<CoinType> | Object<Metadata> (an object address) | | Storage | CoinStore<T> under accounts | FungibleStore objects; primary store per account | | Capabilities | MintCapability, BurnCapability, FreezeCapability | MintRef, BurnRef, TransferRef | | Customization | Limited | Dispatchable hooks (custom withdraw/deposit logic) | | Status | Legacy, migrating | Recommended |
33.5 Creating a fungible asset on Aptos
Users transfer with primary_fungible_store::transfer(&sender, metadata, to, amount), and wallets read balances with primary_fungible_store::balance(owner, metadata).
module my_addr::gold {
use std::option;
use std::signer;
use std::string::utf8;
use aptos_framework::object::{Self, Object};
use aptos_framework::fungible_asset::{Self, MintRef, BurnRef, TransferRef, Metadata};
use aptos_framework::primary_fungible_store;
const ASSET_SYMBOL: vector<u8> = b"GOLD";
const E_NOT_ADMIN: u64 = 1;
#[resource_group_member(group = aptos_framework::object::ObjectGroup)]
struct Refs has key {
mint_ref: MintRef,
burn_ref: BurnRef,
transfer_ref: TransferRef,
}
fun init_module(admin: &signer) {
let constructor_ref = &object::create_named_object(admin, ASSET_SYMBOL);
primary_fungible_store::create_primary_store_enabled_fungible_asset(
constructor_ref,
option::none(), // max supply (none = unlimited)
utf8(b"Gold Coin"),
utf8(ASSET_SYMBOL),
8, // decimals
utf8(b"https://example.com/gold.png"),
utf8(b"https://example.com"),
);
let metadata_signer = object::generate_signer(constructor_ref);
move_to(&metadata_signer, Refs {
…33.6 Legacy Aptos coin (for reading older code)
struct GOLD {}
// coin::initialize<GOLD>(admin, utf8(b"Gold"), utf8(b"GOLD"), 8, true)
// returns (BurnCapability<GOLD>, FreezeCapability<GOLD>, MintCapability<GOLD>)
// coin::register<GOLD>(user); coin::mint(amount, &mint_cap); coin::deposit(addr, coins)
// aptos_account::transfer_coins<GOLD>(from, to, amount)