Module 0x2::token
The Token module which implements a Closed Loop Token with a configurable policy. The policy is defined by a set of rules that must be satisfied for an action to be performed on the token.
The module is designed to be used with a TreasuryCap to allow for minting and burning of the Tokens. And can act as a replacement / extension or a companion to existing open-loop (Coin) systems.
Module: sui::balance sui::coin sui::token
Main type: Balance<T> Coin<T> Token<T>
Capability: Supply<T> <----> TreasuryCap<T> <----> TreasuryCap<T>
Abilities: store key + store key
The Token system allows for fine-grained control over the actions performed on the token. And hence it is highly suitable for applications that require control over the currency which a simple open-loop system can't provide.
- Resource
Token
- Resource
TokenPolicyCap
- Resource
TokenPolicy
- Struct
ActionRequest
- Struct
RuleKey
- Struct
TokenPolicyCreated
- Constants
- Function
new_policy
- Function
share_policy
- Function
transfer
- Function
spend
- Function
to_coin
- Function
from_coin
- Function
join
- Function
split
- Function
zero
- Function
destroy_zero
- Function
keep
- Function
new_request
- Function
confirm_request
- Function
confirm_request_mut
- Function
confirm_with_policy_cap
- Function
confirm_with_treasury_cap
- Function
add_approval
- Function
add_rule_config
- Function
rule_config
- Function
rule_config_mut
- Function
remove_rule_config
- Function
has_rule_config
- Function
has_rule_config_with_type
- Function
allow
- Function
disallow
- Function
add_rule_for_action
- Function
remove_rule_for_action
- Function
mint
- Function
burn
- Function
flush
- Function
is_allowed
- Function
rules
- Function
spent_balance
- Function
value
- Function
transfer_action
- Function
spend_action
- Function
to_coin_action
- Function
from_coin_action
- Function
action
- Function
amount
- Function
sender
- Function
recipient
- Function
approvals
- Function
spent
- Function
key
use 0x1::option;
use 0x1::string;
use 0x1::type_name;
use 0x2::balance;
use 0x2::coin;
use 0x2::dynamic_field;
use 0x2::event;
use 0x2::object;
use 0x2::transfer;
use 0x2::tx_context;
use 0x2::vec_map;
use 0x2::vec_set;
Resource Token
A single Token with Balance inside. Can only be owned by an address, and actions performed on it must be confirmed in a matching TokenPolicy.
struct Token<T> has key
Fields
- id: object::UID
- balance: balance::Balance<T>
- The Balance of the Token.
Resource TokenPolicyCap
A Capability that manages a single TokenPolicy specified in the for field. Created together with TokenPolicy in the new function.
struct TokenPolicyCap<T> has store, key
Fields
- id: object::UID
- for: object::ID
Resource TokenPolicy
TokenPolicy represents a set of rules that define what actions can be performed on a Token and which Rules must be satisfied for the action to succeed.
- For the sake of availability, TokenPolicy is a key-only object.
- Each TokenPolicy is managed by a matching TokenPolicyCap.
- For an action to become available, there needs to be a record in the rules VecMap. To allow an action to be performed freely, there's an allow function that can be called by the TokenPolicyCap owner.
struct TokenPolicy<T> has key
Fields
- id: object::UID
- spent_balance: balance::Balance<T>
-
The balance that is effectively spent by the user on the "spend"
action. However, actual decrease of the supply can only be done by
the TreasuryCap owner when flush is called.
This balance is effectively spent and cannot be accessed by anyone but the TreasuryCap owner.
- rules: vec_map::VecMap<string::String, vec_set::VecSet<type_name::TypeName>>
- The set of rules that define what actions can be performed on the token. For each "action" there's a set of Rules that must be satisfied for the ActionRequest to be confirmed.
Struct ActionRequest
A request to perform an "Action" on a token. Stores the information about the action to be performed and must be consumed by the confirm_request or confirm_request_mut functions when the Rules are satisfied.
struct ActionRequest<T>
Fields
- name: string::String
- Name of the Action to look up in the Policy. Name can be one of the default actions: transfer, spend, to_coin, from_coin or a custom action.
- amount: u64
- Amount is present in all of the txs
- sender: address
- Sender is a permanent field always
- recipient: option::Option<address>
- Recipient is only available in transfer action.
- spent_balance: option::Option<balance::Balance<T>>
- The balance to be "spent" in the TokenPolicy, only available in the spend action.
- approvals: vec_set::VecSet<type_name::TypeName>
- Collected approvals (stamps) from completed Rules. They're matched against TokenPolicy.rules to determine if the request can be confirmed.
Struct RuleKey
Dynamic field key for the TokenPolicy to store the Config for a specific action Rule. There can be only one configuration per Rule per TokenPolicy.
struct RuleKey<T> has copy, drop, store
Fields
- is_protected: bool
Struct TokenPolicyCreated
An event emitted when a TokenPolicy is created and shared. Because TokenPolicy can only be shared (and potentially frozen in the future), we emit this event in the share_policy function and mark it as mutable.
struct TokenPolicyCreated<T> has copy, drop
Fields
- id: object::ID
- ID of the TokenPolicy that was created.
- is_mutable: bool
- Whether the TokenPolicy is "shared" (mutable) or "frozen" (immutable) - TBD.
Constants
Trying to perform an admin action with a wrong cap.
const ENotAuthorized: u64 = 2;
The balance is too low to perform the action.
const EBalanceTooLow: u64 = 3;
The balance is not zero when trying to confirm with TransferPolicyCap.
const ECantConsumeBalance: u64 = 5;
Rule is trying to access a missing config (with type).
const ENoConfig: u64 = 6;
The rule was not approved.
const ENotApproved: u64 = 1;
The balance is not zero.
const ENotZero: u64 = 4;
The action is not allowed (defined) in the policy.
const EUnknownAction: u64 = 0;
Using confirm_request_mut without spent_balance. Immutable version of the function must be used instead.
const EUseImmutableConfirm: u64 = 7;
A Tag for the from_coin action.
const FROM_COIN: vector<u8> = [102, 114, 111, 109, 95, 99, 111, 105, 110];
A Tag for the spend action.
const SPEND: vector<u8> = [115, 112, 101, 110, 100];
A Tag for the to_coin action.
const TO_COIN: vector<u8> = [116, 111, 95, 99, 111, 105, 110];
A Tag for the transfer action.
const TRANSFER: vector<u8> = [116, 114, 97, 110, 115, 102, 101, 114];
Function new_policy
Create a new TokenPolicy and a matching TokenPolicyCap. The TokenPolicy must then be shared using the share_policy method.
TreasuryCap guarantees full ownership over the currency, and is unique, hence it is safe to use it for authorization.
public fun new_policy<T>(_treasury_cap: &coin::TreasuryCap<T>, ctx: &mut tx_context::TxContext): (token::TokenPolicy<T>, token::TokenPolicyCap<T>)
Implementation
public fun new_policy<T>(
_treasury_cap: &TreasuryCap<T>, ctx: &mut TxContext
): (TokenPolicy<T>, TokenPolicyCap<T>) {
let policy = TokenPolicy {
id: object::new(ctx),
spent_balance: balance::zero(),
rules: vec_map::empty()
};
let cap = TokenPolicyCap {
id: object::new(ctx),
`for`: object::id(&policy)
};
(policy, cap)
}
Function share_policy
Share the TokenPolicy. Due to key-only restriction, it must be shared after initialization.
public fun share_policy<T>(policy: token::TokenPolicy<T>)
Implementation
public fun share_policy<T>(policy: TokenPolicy<T>) {
event::emit(TokenPolicyCreated<T> {
id: object::id(&policy),
is_mutable: true,
});
transfer::share_object(policy)
}
Function transfer
Transfer a Token to a recipient. Creates an ActionRequest for the "transfer" action. The ActionRequest contains the recipient field to be used in verification.
public fun transfer<T>(t: token::Token<T>, recipient: address, ctx: &mut tx_context::TxContext): token::ActionRequest<T>
Implementation
public fun transfer<T>(
t: Token<T>, recipient: address, ctx: &mut TxContext
): ActionRequest<T> {
let amount = t.balance.value();
transfer::transfer(t, recipient);
new_request(
transfer_action(),
amount,
option::some(recipient),
option::none(),
ctx
)
}
Function spend
Spend a Token by unwrapping it and storing the Balance in the ActionRequest for the "spend" action. The ActionRequest contains the spent_balance field to be used in verification.
Spend action requires confirm_request_mut to be called to confirm the request and join the spent balance with the TokenPolicy.spent_balance.
public fun spend<T>(t: token::Token<T>, ctx: &mut tx_context::TxContext): token::ActionRequest<T>
Implementation
public fun spend<T>(t: Token<T>, ctx: &mut TxContext): ActionRequest<T> {
let Token { id, balance } = t;
id.delete();
new_request(
spend_action(),
balance.value(),
option::none(),
option::some(balance),
ctx
)
}
Function to_coin
Convert Token into an open Coin. Creates an ActionRequest for the "to_coin" action.
public fun to_coin<T>(t: token::Token<T>, ctx: &mut tx_context::TxContext): (coin::Coin<T>, token::ActionRequest<T>)
Implementation
public fun to_coin<T>(
t: Token<T>, ctx: &mut TxContext
): (Coin<T>, ActionRequest<T>) {
let Token { id, balance } = t;
let amount = balance.value();
id.delete();
(
balance.into_coin(ctx),
new_request(
to_coin_action(),
amount,
option::none(),
option::none(),
ctx
)
)
}
Function from_coin
Convert an open Coin into a Token. Creates an ActionRequest for the "from_coin" action.
public fun from_coin<T>(coin: coin::Coin<T>, ctx: &mut tx_context::TxContext): (token::Token<T>, token::ActionRequest<T>)
Implementation
public fun from_coin<T>(
coin: Coin<T>, ctx: &mut TxContext
): (Token<T>, ActionRequest<T>) {
let amount = coin.value();
let token = Token {
id: object::new(ctx),
balance: coin.into_balance()
};
(
token,
new_request(
from_coin_action(),
amount,
option::none(),
option::none(),
ctx
)
)
}