Fungible tokens on Sui
A summary of fungible tokens on Sui with links to key resources
Overview
This doc provides an overview of the different types of fungible tokens on the Sui blockchain. It's meant for both non-technical and technical audiences - giving an overview and then linking to more code examples and resources for developers who want to learn more. To learn about non-fungible tokens (NFTs) on Sui, see our guide.
Terms: Often, the native currency of a chain is referred to as a coin (e.g. SUI for the Sui blockchain), and currencies that live on that chain are referred to as tokens (e.g. the BLUE token we reference below). However, Sui's API and standards refer to some tokens under this definition as coins. Because of this and because of the fact that both types are fungible tokens - unlike NFTs, if you and I have 10 of them there's no difference between our 10 - we refer to all cases as fungible tokens.
Types of tokens on Sui
SUI
The native currency of the Sui blockchain. Used to pay the gas fees for transactions and often used to pay for other things like NFTs - including this interesting-looking creature listed for 44 SUI:

In addition to buying assets and paying for gas, you can stake your SUI with a validator, helping to secure the Sui network in exchange for staking rewards. Sui is a delegated proof-of-stake blockchain, meaning value in the form of staked SUI secures the network (unlike Bitcoin, which is proof of work, meaning value in the form of computing resources secures the network).
Like the tokens of other blockchains, the price of SUI can vary a lot over time. This variance means that gas and NFT prices can fluctuate day-to-day in fiat terms. Not all tokens on Sui have this variance, as you'll see in the next section.
Stablecoins
Stablecoins are tokens that are pegged to an underlying asset, often a fiat currency like the US dollar. They are an effort to put value on chain without the price fluctuations that tokens like SUI experience. This provides safer ways to perform certain financial transactions, combining the speed and low fees of the Sui blockchain with the relative stability of certain fiat currencies. For example, international remittance payments and a coffee shop taking payment for coffee both benefit from knowing that the value transmitted one day won't be worth, for example, 5% less the next day.
Stablecoins can be bridged from another chain (like USDC bridged from the Ethereum blockchain) or native to Sui (like native USDC). Native stablecoins are generally safer because they don't have the additional trust assumptions that a bridge has (you have to trust the code and the team behind the bridge, in addition to the team and the code behind the stable coin).
Other tradable tokens
Using the Coin Standard, developers can make new fungible tokens on the Sui blockchain that can be traded on exchanges and used by wallets and apps in a fashion similar to SUI. It's fairly common for apps to have a token as a way to provide token holders with governance power to help shape the future of the app, as well as provide financial/reward incentives (for example Bluefin's BLUE token, here on Coinmarketcap). In addition to tokens created by apps like Bluefin, there is also the realm of memecoins. This is not a plug for certain tokens or financial advice - just examples of the types of fungible tokens you'll see. Apps like Sui Coins have made it easy for non-developers to create tokens as well. You can see a list of all the coins on Sui here.
Tokens that follow the Coin Standard can have the added restriction of an address deny list that lists the addresses that can't use the token in transactions. This would be to meet regulatory requirements, such as for a stablecoin.
Closed-loop tokens
Using the Closed-Loop Token Standard, you can make fungible tokens on the Sui blockchain that cannot be used as freely as SUI or Coin Standard tokens. For example, you can limit the applications and wallet addresses that can use the token, as well as set custom rules for transfers, spends, and conversions.
One example is a currency for a game that is only to be used within said game (and not bought and sold on external exchanges, where non-players can manipulate your in-game economy and throw it off balance). Another example is an app that rewards customers with loyalty tokens but only wants the tokens to be used by certain services and accounts. Finally, an app that requires KYC checks for legal purposes could restrict the use of tokens to accounts that have completed those checks. You can read more examples in this blog post.
Creating and using fungible tokens
Open-Loop Coin Standard
As described by the Sui Foundation, "The Coin standard is the technical standard used for smart contracts on Sui for creating coins on the Sui blockchain. 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." You can also create regulated coins (like stablecoins) that have a deny list of addresses that can't use them, and even a global on/off switch that can prevent everyone from using them until you turn the switch back to "on"!
Here is a very simple example (modified from a Sui Foundation example). We're minting the best memecoin ever!
module coin::coin;
use sui::coin::{Self, TreasuryCap};
use sui::url;
public struct COIN has drop {}
fun init(witness: COIN, ctx: &mut TxContext) {
let (treasury, metadata) = coin::create_currency(
witness,
6,
b"BMEME",
b"Best Meme",
b"This is the best meme coin ever!",
option::some(url::new_unsafe_from_bytes(b"https://www.pexels.com/photo/close-up-photo-of-dog-wearing-sunglasses-1629781/")),
ctx,
);
transfer::public_freeze_object(metadata);
transfer::public_transfer(treasury, ctx.sender())
}
public fun mint(
treasury_cap: &mut TreasuryCap<COIN>,
amount: u64,
recipient: address,
ctx: &mut TxContext,
) {
let coin = coin::mint(treasury_cap, amount, ctx);
transfer::public_transfer(coin, recipient)
}
After publishing this to testnet, we were able to make a mint call via the CLI to mint coins to an address.
sui client call --package 0x50d3049f4a36fe6ca263e29dbc75b441378487fb2c955ae4b15b7bb50efcea36 --module coin --function mint --args 0xe9ba0ad7186394948c259da1a6a29e5b406c7e988c8cf65cabc480a2eabad523 1000 0xeb583665fb5a35f488234bd8715be86bab0f8ba5311d95ee5a8f67b6088ca2b0
Here is the transaction showing the recipient getting 10.0 BMEME
(or, Best Meme) coin.
Closed-Loop Token Standard
The Closed-Loop Token Standard lets you limit the applications that can use the token (say, just your app, or your app and a partner app). Closed-loop tokens don't allow transfers, conversions, and spends by default. You can set up custom rules that allow some of these actions under certain conditions based on your needs. This is useful if, say, you have an in-game economy or a loyalty rewards token that you don't want to be openly sold on exchanges in an unrestricted fashion.
As an example, this Sui Foundation sample project creates a token with a rule that it can only be used at this module's gift shop. In the buy_a_gift
method, approval is given for the spend. Key parts of the sample project are shown below:
/// The OTW for the Token / Coin.
public struct LOYALTY has drop {}
/// This is the Rule requirement for the `GiftShop`.
public struct GiftShop has drop {}
public struct Gift has key, store {
id: UID,
}
// Create a new LOYALTY currency, create a `TokenPolicy` for it.
fun init(otw: LOYALTY, ctx: &mut TxContext) {
let (treasury_cap, coin_metadata) = coin::create_currency(
otw,
0, // no decimals
b"LOY", // symbol
b"Loyalty Token", // name
b"Token for Loyalty", // description
option::none(), // url
ctx,
);
let (mut policy, policy_cap) = token::new_policy(&treasury_cap, ctx);
// Enforce that the token can only be used spent at this shop
token::add_rule_for_action<LOYALTY, GiftShop>(
&mut policy,
&policy_cap,
token::spend_action(),
ctx,
);
token::share_policy(policy);
transfer::public_freeze_object(coin_metadata);
transfer::public_transfer(policy_cap, tx_context::sender(ctx));
transfer::public_transfer(treasury_cap, tx_context::sender(ctx));
}
... // more module code
/// Buy a gift for 10 tokens. The `Gift` is received, and the `Token` is
/// spent (stored in the `ActionRequest`'s `burned_balance` field).
public fun buy_a_gift(token: Token<LOYALTY>, ctx: &mut TxContext): (Gift, ActionRequest<LOYALTY>) {
assert!(token::value(&token) == GIFT_PRICE, EIncorrectAmount);
let gift = Gift { id: object::new(ctx) };
let mut req = token::spend(token, ctx);
// only required because we've set this rule
token::add_approval(GiftShop {}, &mut req, ctx);
(gift, req)
}
Using Stablecoins
To use stablecoins in your app, see this guide. Here, we show a simple example for native USDC taken from this Mysten sample code (with comments and a slight modification we've added).
module usdc_usage::example;
use sui::coin::Coin;
use sui::sui::SUI;
use usdc::usdc::USDC;
public struct Sword has key, store {
id: UID,
strength: u64
}
public fun buy_sword_with_usdc(
coin: Coin<USDC>,
tx_context: &mut TxContext
): Sword {
// We're creating a sword that's as strong as the amount of money the user
// spends. Since USDC has six decimal places, a $1 USDC sword would
// have a strength of 1,000,000!
// See: https://docs.sui.io/references/framework/sui/coin#sui_coin_value
let sword = create_sword(coin.value(), tx_context);
// After making the sword, we transfer the sender's USDC to oursleves
// See: https://docs.sui.io/references/framework/sui/transfer#sui_transfer_public_transfer
transfer::public_transfer(coin, @0xYOUR_TREASURY_ADDRESS);
// At some point we need to transfer the sword to the sender or they'll
// be very unhappy. Here, we assume that another function calls this
// one and handles the transfer step.
sword
}
fun create_sword(strength: u64, tx_context: &mut TxContext): Sword {
let id = object::new(tx_context);
Sword { id, strength }
}
... // more module code below
Reading token data to show in your app
Fetch an address's coins
The main call is suix_getAllCoins
for coins of all types owned by a user. You can also use suix_getCoins
request if you only care about one coin type.
Each coin is represent as an individual object in Sui. For example, an address with SUI has a set of 0-many SUI coin objects that add up to its total balance. Here is a partial response showing one coin object - a SUI coin worth 1999995900 MIST
, or 1.99 SUI
, and another two worth 100 MIST
(so, balance
here means the balance of a coin object and not the address's overall SUI balance, which is higher).
Partial cURL response
{
"id" : 1,
"jsonrpc" : "2.0",
"result" : {
"data" : [
{
"balance" : "1999995900",
"coinObjectId" : "0x5e0807e912974b727b6d3bc05a139ed0ff49370432283ea5b0705bc697366f08",
"coinType" : "0x2::sui::SUI",
"digest" : "6ZHZiiqfDmoWrHiMBsfQwWtrsWS5eNzwwBEyjH4nQxWy",
"previousTransaction" : "BeRn1PNWq7QhucWnv2ohk6watpbkxSR63p5EAwgScSdo",
"version" : "101882055"
},
{
"balance" : "100",
"coinObjectId" : "0x050a6a7496e71d3354d0d2d9e32ac60d66297755ad5c871ec198a429b3d56bed",
"coinType" : "0x2::sui::SUI",
"digest" : "FYXD5ijJRg43VoP8ZWcqYhszDfPGiNVV9sQ81bx5WLtz",
"previousTransaction" : "G45zwUKQLPMZ9nZy4WwQn9rJiL8S5prKbN1J1vnugrNh",
"version" : "101882046"
},
...
Get the icon and other data for a coin
You can use suix_getCoinMetadata
. Here is the result for coin"0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"
( native USDC) on Mainnet:
Partial cURL response
{
"id" : 1,
"jsonrpc" : "2.0",
"result" : {
"decimals" : 6,
"description" : "USDC is a US dollar-backed stablecoin issued by Circle. USDC is designed to provide a faster, safer, and more efficient way to send, spend, and exchange money around the world.",
"iconUrl" : "https://circle.com/usdc-icon",
"id" : "0x69b7a7c3c200439c1b5f3b19d7d495d5966d5f08de66c69276152f8db3992ec6",
"name" : "USDC",
"symbol" : "USDC"
}
}
As described here, in addition to the name, symbol, and description, we're also given an icon URL to fetch the icon for display in a DeFi app or wallet, say - which in this case is
. We're also told that the token uses six decimals (whereas SUI uses 9, since 1 billion MIST = 1 SUI).
A helpful community project
A tool that's worth looking into if you have to regularly fetch data about coins is CoinMeta, which exists to solve two issues:
- Some coins do not return image data in the above request, or they do but the image is not in a web-optimized format for displaying a small logo.
- If you want info about a lot of coins, then you have to make a JSON-RPC
suix_getCoinMetadata
request for each one (where this project has an endpoint that can return info for multiple coins in one response).
Get the total supply for a coin
You can use sui_getTotalSupply
for this. Here is the result for "0xdba34672e30cb065b1f93e3ab55318768fd6fef66c15942c9f7cb846e2f900e7::usdc::USDC"
( native USDC) on Mainnet:
Partial cURL response
{
"id" : 1,
"jsonrpc" : "2.0",
"result" : {
"value" : "319585979218700"
}
}
Recall from the section above that the token uses 6 decimals. So, the total amount of native USDC on the Sui blockchain at the time of writing this is 319,585,979.218700
.
Resources
Here, we list many of the resources we listed above in a compact form:
General
- All open-loop fungible tokens on Sui (including SUI and stablecoins).
SUI
- SUI Tokenomics (including docs on staking and how gas prices are calculated).
- Staking SUI.
- How gas fees are calculated for transactions.
Open-loop tokens
- Sui Coin Standard: “the technical standard used for smart contracts on Sui for creating coins on the Sui blockchain.”
- Developer guide for creating coins with an address-deny list (a requirement for coins like stablecoins)
Closed-loop tokens
Closed-Loop Tokens let you limit the applications that can use a certain token. Useful for applications like loyalty/reward points, complying with certain regulations, or creating a closed in-game economy.
- Standard
- Blog post summarizing the benefits and giving example use-cases.
- Developer guides for creating an in-game currency and a loyalty token
Stablecoins
- Developer guide for using Stablecoins
- List of stablecoins on Sui, both bridged and native
Updated 11 days ago