docs/content/standards/currency.mdx
:::tip
The Coin and Currency standards are both used to create fungible tokens. However, they use different creation methods and store metadata in a different type of object.
Coin creates assets using coin::create_currency while Currency uses coin_registry::new_currency and coin_registry::new_currency_with_otw.
Coin uses CoinMetadata while Currency uses Currency object. Fungible tokens created on Sui using the Coin or Currency standard are referred to as coins.
Coin refers to a single object wrapper for a fungible asset, while the term currency refers to the object that is created in CoinRegistry, which describes the currency properties and setup.
For fungible tokens created on Sui using the Closed-Loop Token standard, the documentation uses the term tokens. In practice, the terms for both these objects are often interchangeable.
:::
The Currency Standard is a technical standard used by Move smart contracts for creating coins on Sui. The sui::coin_registry module provides the logic that defines the standard, unifies metadata, enhances supply tracking, and improves regulatory features.
The standardization of coin creation on Sui means that wallets, exchanges, and other smart contracts can manage coins created on Sui the same as they manage SUI, without any additional processing logic.
See Sui Tokenomics to learn more about the SUI native currency and its use on the network.
Coins on Sui can offer specialized abilities while following the Currency Standard. For example, you can create regulated coins that allow their creator to add specific addresses to a deny list, so that the identified addresses cannot use the coin as inputs to transactions.
The Currency<T> type represents open-loop fungible tokens (see Token<T> for closed-loop tokens). Currencies are denominated by their type parameter, T, which is also associated with metadata (like name, symbol, decimal precision, and so on) that applies to all instances of Currency<T>. The sui::coin_registry module exposes an interface over Currency<T> that treats it as fungible, meaning that a unit of T held in one instance of Currency<T> is interchangeable with any other unit of T, much like how traditional fiat currencies operate.
The Coin Registry is a centralized system that provides unified coin management through the sui::coin_registry module.
:::info
The registry is a system-level shared object located at address 0xc.
:::
<details> <summary> `sui::coin_registry` module </summary> <ImportContent source="crates/sui-framework/packages/sui-framework/sources/registries/coin_registry.move" mode="code" /> </details>CoinRegistry: The main system object that coordinates all coin-related operations. This shared object serves as the entry point for all registry operations and is created once during network initialization. Its address is 0xc.
Currency<T>: The core of the registry system, storing comprehensive information about each coin type including:
TreasuryCaps and MetadataCaps for the coin type.The registry supports three different supply management models:
TreasuryCap holder controls minting and burning.Coins can have different regulatory states:
DenyCapV2 that can restrict addresses from using it.The registry supports two different coin creation flows:
Standard creation (recommended):
Use the new_currency function at any time after the coin type is published. The function immediately creates a shared Currency<T> object. The type T must be a key-only type, as in public struct MyCoin has key { id: UID }.
One-Time Witness (OTW) creation:
:::caution Proper creation and RPC support requires a second transaction to promote the currency to the registry.
OTW creation of a new coin is a two-step process. The initialization process begins with package publication. Then, a call to coin_registry::finalize_registration is needed to place the coin into the registry.
See Coin finalization for more information.
:::
The new_currency_with_otw function uses an OTW for uniqueness proof. See One-Time Witness in The Move Book for more information.
Both creation methods return a CurrencyInitializer<T> that allows for additional configuration:
Currency created with the new_currency function is immediately complete after the finalize function is called.
:::caution OTW currencies
For OTW created currencies, you must call finalize_registration to promote the currency to the registry.
:::
If you publish a package that uses an OTW to create a coin in the package init function, then a call must be made to coin_registry::finalize_registration after initialization. The function transforms the coin into a shared object.
:::caution OTW currencies
For OTW created currencies, you must call finalize_registration to promote the currency to the registry.
:::
# Requires using the ID of the Currency object created during publishing.
# This step is only required for OTW created currencies.
sui client ptb
--assign @created_currency_object_id currency_to_promote
--move-call 0x2::coin_registry::finalize_registration <CURRENCY_CYPE> @0xc currency_to_promote
When you create a coin using the coin_registry::new_currency or coin_registry::new_currency_with_otw functions, the publisher of the smart contract that creates the coin receives a TreasuryCap object upon finalization of the coin. The TreasuryCap object is required to mint new coins or to burn current ones (depending on coin supply state). Consequently, only addresses that have access to this object can maintain the coin supply on the Sui network.
The TreasuryCap object is transferable, so a third party can take over the management of a coin that you create if you transfer the TreasuryCap to them. After transferring the object, you can no longer mint and burn coins yourself.
The Currency Standard supports creating regulated coins. Use the make_regulated function during the initialization phase before calling finalize. The function adds deny list capabilities to the Currency<T> and tracks the regulatory status within the Coin Registry. The function returns a DenyCap that allows the bearer to maintain the list of addresses on the deny list.
DenyList objectThe list of addresses that cannot use a particular regulated coin is held within a system-created DenyList shared object. If you have access to the DenyCap, then you can use the coin::deny_list_v2_add and coin::deny_list_v2_remove functions to add and remove addresses.
Regulated coin objects include an allow_global_pause Boolean field. When set to true, the bearer of the DenyCapV2 object for the coin type can use the coin::deny_list_v2_enable_global_pause function to pause coin activity indefinitely. Immediately upon the bearer initiating the pause, the network disallows the coin type as input for any transactions. At the start of the next epoch (epochs last approximately 24 hours), the network additionally disallows all addresses from receiving the coin type.
When the bearer of the DenyCapV2 object for the coin type removes the pause using coin::deny_list_v2_disable_global_pause, the coins are immediately available to use again as transaction inputs. Addresses cannot receive the coin type, however, until the following epoch.
The global pause functionality does not affect the deny list for the coin. After clearing the pause for the coin, any addresses included in the deny list are still unable to interact with the coin.
Currency metadata is stored centrally within the Currency<T> object in the registry. Metadata updates are controlled by the MetadataCap<T> capability, which can be:
MetadataCap has not yet been claimed.MetadataCap has been claimed and can be used for updates.MetadataCap has been permanently deleted, preventing future updates.The fields of the metadata objects include the following:
| Name | Description |
|---|---|
registry | The CoinRegistry system object ID (0x3). |
decimals | The number of decimals the coin uses. If you set this field to 3, then a coin of value 1000 displays as 1.000. |
name | Name of the coin. |
symbol | Symbol for the coin. This might be the same as name, but is typically fewer than 5 all capital letters. For example, SUI is the symbol for the native coin on Sui but its name is also SUI. |
description | A short description to describe the coin. |
icon_url | The URL for the coin's icon, used for display in wallets, explorers, and other apps. |
The Coin Registry supports advanced supply management models:
TreasuryCap.Use the coin::mint function to create new coins.
The signature shows that a Coin<T> results from calling the function with a TreasuryCap, value for the coin created, and the transaction context. The function updates the total supply in TreasuryCap automatically. Upon display, the coin value respects the decimals value in the metadata. So, if you supply 1000000 as the coin value that has a decimal value of 6, the coin's value displays as 1.000000.
Coins with a burn-only supply status can be burned directly through the registry using coin_registry::burn and coin_registry::burn_balance functions without requiring the TreasuryCap.
For coins with an unknown supply status, use the coin::burn function to destroy current coins.
The signature shows that only the TreasuryCap and coin object you want to burn are necessary inputs, returning the amount by which the supply was decreased (value of the coin). The function does not allow you to burn more coins than are available in the supply.
The deny list is only applicable to regulated coins. As mentioned previously, when you create a regulated coin you receive a DenyCapV2 that authorizes the bearer to add and remove addresses from the system-created DenyList object. Any address on the list for your coin cannot use the coin as an input to transactions starting immediately upon being added. At the epoch that follows address addition to the deny list, the addresses additionally cannot receive the coin type. In other words, an address that gets added to the deny list for a coin type is immediately unable to send the coin. At the start of the following epoch, the address is still unable to send the coin but is also unable to receive it. From that point, the address cannot interact with the coin until expressly removed from the deny list by the DenyCapV2 bearer.
You can query information directly from the Currency<T> object.
decimals<T>(): Get number of decimals.
name<T>(): Get coin name.
symbol<T>(): Get coin symbol.
description<T>(): Get coin description.
icon_url<T>(): Get icon URL.
total_supply<T>(): Get current total supply.
is_supply_fixed<T>(): Check if supply is fixed.
is_supply_burn_only<T>(): Check if supply is burn-only.
is_metadata_cap_claimed<T>(): Check if metadata cap is claimed.
is_metadata_cap_deleted<T>(): Check if metadata cap is deleted.
treasury_cap_id<T>(): Get treasury cap object ID.
metadata_cap_id<T>(): Get metadata cap object ID.
is_regulated<T>(): Check if coin is regulated.
deny_cap_id<T>(): Get deny cap object ID.
Metadata updates require the MetadataCap<T> object, which is only available to the bearer of the TreasuryCap for a coin. The bearer of the TreasuryCap can claim the MetadataCap using the claim_metadata_cap function only once. Currency tracks the claimed status of the capability through its metadata_cap_id field.
The bearer of the MetadataCap can use the available getter functions to update values.
set_name<T>(): Update coin name.
set_description<T>(): Update coin description.
set_icon_url<T>(): Update icon URL.
Manage the metadata capability using:
claim_metadata_cap<T>(): Claim the metadata capability one time.
delete_metadata_cap<T>(): Permanently delete the capability to prevent future updates.
:::caution
Deleting the MetadataCap using delete_metadata_cap<T>() is an irreversible action.
:::
Capability security:
MetadataCap: Protect metadata capabilities as they control coin branding.TreasuryCap: Treasury capabilities determine minting and burning permissions.DenyCapV2: Deny capabilities can restrict coin usage.Validation: The registry enforces several important validations:
Migration safety: