Create Currencies and Tokens
The Coin Registry system provides a centralized approach to currency management through the sui::coin_registry
module. The registry is a shared object located at address 0xc
that stores metadata, supply information, and regulatory status for all registered coin types.
The sui::token
module handles token creation on the network. Refer to the Closed-Loop Token and Currency Standards documentation for more information on these features.
Currency creation process
The registry system supports two currency creation methods:
- Standard creation: Call
new_currency<T>()
when creating the coin outside of theinit
function of your package. - OTW creation: Use
new_currency_with_otw<T>()
with a One-Time Witness for uniqueness proof.
Both methods return a CurrencyInitializer<T>
that allows configuration before finalization:
Regular currency creation
/// Implements a coin with a deflationary supply. Upon initialization, mint the
/// total supply and give up `TreasuryCap` to make the supply deflationary (prevents
/// minting but allows burning).
///
/// Keep the ability to update Currency metadata, such as name, symbol,
/// description, and icon URL.
module examples::currency;
use sui::coin::Coin;
use sui::coin_registry::{Self, CoinRegistry};
// Total supply of the `DEFLATIONARY_SUPPLY` coin is 1B (with 6 decimals).
const TOTAL_SUPPLY: u64 = 1000000000_000000;
// The type identifier of coin. The coin will have a type
// tag of kind: `Coin<package_object::currency::MyCoin>`
public struct MyCoin has key { id: UID }
#[allow(lint(self_transfer))]
/// Creates a new currency with a non-OTW proof of uniqueness.
public fun new_currency(registry: &mut CoinRegistry, ctx: &mut TxContext): Coin<MyCoin> {
let (mut currency, mut treasury_cap) = coin_registry::new_currency(
registry,
6, // Decimals
b"MyCoin".to_string(), // Symbol
b"My Coin".to_string(), // Name
b"Standard Unregulated Coin".to_string(), // Description
b"https://example.com/my_coin.png".to_string(), // Icon URL
ctx,
);
let total_supply = treasury_cap.mint(TOTAL_SUPPLY, ctx);
currency.make_supply_burn_only(treasury_cap);
let metadata_cap = currency.finalize(ctx);
transfer::public_transfer(metadata_cap, ctx.sender());
total_supply
}
OTW currency creation
module examples::my_coin_new;
use sui::coin_registry;
// The type identifier of coin. The coin will have a type
// tag of kind: `Coin<package_object::mycoin::MYCOIN>`
// Make sure that the name of the type matches the module's name.
public struct MY_COIN_NEW has drop {}
// Module initializer is called once on module publish. A `TreasuryCap` is sent
// to the publisher, who then controls minting and burning. `MetadataCap` is also
// sent to the Publisher.
fun init(witness: MY_COIN_NEW, ctx: &mut TxContext) {
let (builder, treasury_cap) = coin_registry::new_currency_with_otw(
witness,
6, // Decimals
b"MY_COIN".to_string(), // Symbol
b"My Coin".to_string(), // Name
b"Standard Unregulated Coin".to_string(), // Description
b"https://example.com/my_coin.png".to_string(), // Icon URL
ctx,
);
let metadata_cap = builder.finalize(ctx);
// Freezing this object makes the metadata immutable, including the title, name, and icon image.
// If you want to allow mutability, share it with public_share_object instead.
transfer::public_transfer(treasury_cap, ctx.sender());
transfer::public_transfer(metadata_cap, ctx.sender());
}
The initialization process allows for:
- Supply model selection: Choose fixed, burn-only, or flexible supply.
- Regulatory configuration: Add deny list capabilities if needed.
After initialization of a currency using the OTW method, you must call finalize_registration
to create the shared Currency
object that the Coin Registry can track.
DenyList
The Sui framework provides a DenyList
singleton, shared object that the bearer of a DenyCapV2
can access to specify a list of addresses that are unable to use a Sui core type. The initial use case for DenyList
, however, focuses on limiting access to coins of a specified type. This is useful, for example, when creating a regulated coin on Sui that requires the ability to block certain addresses from using it as inputs to transactions. Regulated coins on Sui satisfy any regulations that require the ability to prevent known bad actors from having access to those coins.
The DenyList
object is a system object that has the address 0x403
. You cannot create it yourself.
Create regulated currency
Use the make_regulated()
function during the initialization phase before calling finalize()
. This adds deny list capabilities to the Currency<T>
and tracks the regulatory status within the registry system.
Regulated currency creation
module examples::regcoin_new;
use sui::coin::{Self, DenyCapV2};
use sui::coin_registry;
use sui::deny_list::DenyList;
public struct REGCOIN_NEW has drop {}
fun init(witness: REGCOIN_NEW, ctx: &mut TxContext) {
let (mut currency, treasury_cap) = coin_registry::new_currency_with_otw(
witness,
6, // Decimals
b"REGCOIN".to_string(), // Symbol
b"Regulated Coin".to_string(), // Name
b"Currency with DenyList Support".to_string(), // Description
b"https://example.com/regcoin.png".to_string(), // Icon URL
ctx,
);
// Claim `DenyCapV2` and mark currency as regulated.
let deny_cap = currency.make_regulated(true, ctx);
let metadata_cap = currency.finalize(ctx);
let sender = ctx.sender();
transfer::public_transfer(treasury_cap, sender);
transfer::public_transfer(metadata_cap, sender);
transfer::public_transfer(deny_cap, sender)
}
public fun add_addr_from_deny_list(
denylist: &mut DenyList,
denycap: &mut DenyCapV2<REGCOIN_NEW>,
denyaddy: address,
ctx: &mut TxContext,
) {
coin::deny_list_v2_add(denylist, denycap, denyaddy, ctx);
}
public fun remove_addr_from_deny_list(
denylist: &mut DenyList,
denycap: &mut DenyCapV2<REGCOIN_NEW>,
denyaddy: address,
ctx: &mut TxContext,
) {
coin::deny_list_v2_remove(denylist, denycap, denyaddy, ctx);
}
Create tokens
Tokens reuse the TreasuryCap
defined in the sui::coin
module and therefore have the same initialization process. The coin::create_currency
function guarantees the uniqueness of the TreasuryCap
and forces the creation of a CoinMetadata
object.
Coin-like functions perform the minting and burning of tokens. Both require the TreasuryCap
:
token::mint
: Mint a tokentoken::burn
: Burn a token
See Closed-Loop Token standard for complete details of working with tokens.
Related links
You can create regulated currencies on Sui using the Coin Registry system. These coins include the ability to control access using a deny list.
Use the Sui Closed-Loop Token standard to create tokens that are only valid within specific workflows and services. One example of Closed-Loop Tokens is a loyalty token.
Use the Sui Closed-Loop Token standard to create tokens that you can use as currency within a game application.
Derived objects enable deterministic object addresses, Transfer-to-Object capabilities, guaranteed uniqueness, and native parallelization for building scalable composable systems on Sui.
The Move Book documentation of the one time witness pattern.