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)