Closed-Loop Token
Using the Closed-Loop Token standard, you can limit the applications that can use the token and set up custom policies for transfers, spends, and conversions. The sui::token
module in the Sui framework defines the standard.
Background and use cases
The Coin standard on Sui is an example of an open-loop system - coins are free-flowing, wrappable, freely transferable and you can store them in any application. The best real world analogy would be cash - hardly regulated and can be freely used and passed.
Some applications, however, require constraining the scope of the token to a specific purpose. For example, some applications might need a token that you can only use for a specific service, or that an authorized account can only use, or a token that you can block certain accounts from using. A real-world analogy would be a bank account - regulated, bank-controlled, and compliant with certain rules and policies.
Difference with coins
Unlike coins, which have key + store
abilities and thus support wrapping and public transfers, tokens have only the key
ability and you cannot wrap them, store them as a dynamic field, or freely transfer them (unless you include a custom policy for that). Consequently, only accounts can own tokens. You can't store them in an application (however, you can spend them, as detailed in the Spending section).
// defined in `sui::coin`
struct Coin<phantom T> has key, store { id: UID, balance: Balance<T> }
// defined in `sui::token`
struct Token<phantom T> has key { id: UID, balance: Balance<T> }
Compliance and rules
You can set up any rules for transfers, spends, and conversions for the tokens you create. You specify these rules per action in the TokenPolicy. Rules are custom programmable restrictions that you can use to implement any request authorization or validation logic.
For example, a policy can set a limit on a transfer - X
tokens per operation; or require user verification before spending tokens; or allow spending tokens only on a specific service.
You can reuse rules across different policies and applications; and you can freely combine rules to create complex policies.
Public actions
Tokens have a set of public and protected actions that you can use to manage the token. Public actions are available to everyone and don't require any authorization. They have similar APIs to coins, but operate on the Token
type:
token::keep
- send a token to the transaction sendertoken::join
- join two tokenstoken::split
- split a token into two, specify the amount to splittoken::zero
- create an empty (zero balance) tokentoken::destroy_zero
- destroy a token with zero balance
See Coin Token Comparison for coin and token methods comparison.
Protected actions
Protected actions are ones that issue an ActionRequest
- a hot-potato struct that must be resolved for the transaction to succeed. There are three main ways to resolve an ActionRequest
, most common of which is via the TokenPolicy
.
token::transfer
- transfer a token to a specified addresstoken::to_coin
- convert a token to a cointoken::from_coin
- convert a coin to a tokentoken::spend
- spend a token on a specified address
The previous methods are included in the base implementation, however it is possible to create ActionRequest
s for custom actions.
Token policy and rules
Protected actions are disabled by default but you can enable them in a TokenPolicy
. Additionally, you can set custom restrictions called rules that a specific action must satisfy for it to succeed.