Module 0x1::managed_coin
ManagedCoin is built to make a simple walkthrough of the Coins module. It contains scripts you will need to initialize, mint, burn, transfer coins. By utilizing this current module, a developer can create his own coin and care less about mint and burn capabilities,
- Resource
Capabilities - Constants
- Function
burn - Function
initialize - Function
mint - Function
register - Function
destroy_caps - Function
remove_caps - Specification
use 0x1::coin;
use 0x1::error;
use 0x1::signer;
use 0x1::string;
Resource Capabilities
Capabilities resource storing mint and burn capabilities.
The resource is stored on the account that initialized coin CoinType.
struct Capabilities<CoinType> has key
Fields
-
burn_cap: coin::BurnCapability<CoinType> -
freeze_cap: coin::FreezeCapability<CoinType> -
mint_cap: coin::MintCapability<CoinType>
Constants
Account has no capabilities (burn/mint).
const ENO_CAPABILITIES: u64 = 1;
Function burn
Withdraw an amount of coin CoinType from account and burn it.
public entry fun burn<CoinType>(account: &signer, amount: u64)
Implementation
public entry fun burn<CoinType>(
account: &signer,
amount: u64,
) acquires Capabilities {
let account_addr = signer::address_of(account);
assert!(
exists<Capabilities<CoinType>>(account_addr),
error::not_found(ENO_CAPABILITIES),
);
let capabilities = borrow_global<Capabilities<CoinType>>(account_addr);
let to_burn = coin::withdraw<CoinType>(account, amount);
coin::burn(to_burn, &capabilities.burn_cap);
}
Function initialize
Initialize new coin CoinType in Aptos Blockchain.
Mint and Burn Capabilities will be stored under account in Capabilities resource.
public entry fun initialize<CoinType>(account: &signer, name: vector<u8>, symbol: vector<u8>, decimals: u8, monitor_supply: bool)
Implementation
public entry fun initialize<CoinType>(
account: &signer,
name: vector<u8>,
symbol: vector<u8>,
decimals: u8,
monitor_supply: bool,
) {
let (burn_cap, freeze_cap, mint_cap) = coin::initialize<CoinType>(
account,
string::utf8(name),
string::utf8(symbol),
decimals,
monitor_supply,
);
move_to(account, Capabilities<CoinType> {
burn_cap,
freeze_cap,
mint_cap,
});
}
Function mint
Create new coins CoinType and deposit them into dst_addr’s account.
public entry fun mint<CoinType>(account: &signer, dst_addr: address, amount: u64)
Implementation
public entry fun mint<CoinType>(
account: &signer,
dst_addr: address,
amount: u64,
) acquires Capabilities {
let account_addr = signer::address_of(account);
assert!(
exists<Capabilities<CoinType>>(account_addr),
error::not_found(ENO_CAPABILITIES),
);
let capabilities = borrow_global<Capabilities<CoinType>>(account_addr);
let coins_minted = coin::mint(amount, &capabilities.mint_cap);
coin::deposit(dst_addr, coins_minted);
}
Function register
Creating a resource that stores balance of CoinType on user’s account, withdraw and deposit event handlers.
Required if user wants to start accepting deposits of CoinType in his account.
public entry fun register<CoinType>(account: &signer)
Implementation
public entry fun register<CoinType>(account: &signer) {
coin::register<CoinType>(account);
}
Function destroy_caps
Destroys capabilities from the account, so that the user no longer has access to mint or burn.
public entry fun destroy_caps<CoinType>(account: &signer)
Implementation
public entry fun destroy_caps<CoinType>(account: &signer) acquires Capabilities {
let (burn_cap, freeze_cap, mint_cap) = remove_caps<CoinType>(account);
destroy_burn_cap(burn_cap);
destroy_freeze_cap(freeze_cap);
destroy_mint_cap(mint_cap);
}
Function remove_caps
Removes capabilities from the account to be stored or destroyed elsewhere
public fun remove_caps<CoinType>(account: &signer): (coin::BurnCapability<CoinType>, coin::FreezeCapability<CoinType>, coin::MintCapability<CoinType>)
Implementation
public fun remove_caps<CoinType>(
account: &signer
): (BurnCapability<CoinType>, FreezeCapability<CoinType>, MintCapability<CoinType>) acquires Capabilities {
let account_addr = signer::address_of(account);
assert!(
exists<Capabilities<CoinType>>(account_addr),
error::not_found(ENO_CAPABILITIES),
);
let Capabilities<CoinType> {
burn_cap,
freeze_cap,
mint_cap,
} = move_from<Capabilities<CoinType>>(account_addr);
(burn_cap, freeze_cap, mint_cap)
}
Specification
High-level Requirements
| No. | Requirement | Criticality | Implementation | Enforcement |
|---|---|---|---|---|
| 1 | The initializing account should hold the capabilities to operate the coin. | Critical | The capabilities are stored under the initializing account under the Capabilities resource, which is distinct for a distinct type of coin. | Enforced via initialize. |
| 2 | A new coin should be properly initialized. | High | In the initialize function, a new coin is initialized via the coin module with the specified properties. | Enforced via initialize_internal. |
| 3 | Minting/Burning should only be done by the account who hold the valid capabilities. | High | The mint and burn capabilities are moved under the initializing account and retrieved, while minting/burning | Enforced via: initialize, burn, mint. |
| 4 | If the total supply of coins is being monitored, burn and mint operations will appropriately adjust the total supply. | High | The coin::burn and coin::mint functions, when tracking the supply, adjusts the total coin supply accordingly. | Enforced via TotalSupplyNoChange. |
| 5 | Before burning coins, exact amount of coins are withdrawn. | High | After utilizing the coin::withdraw function to withdraw coins, they are then burned, and the function ensures the precise return of the initially specified coin amount. | Enforced via burn_from. |
| 6 | Minted coins are deposited to the provided destination address. | High | After the coins are minted via coin::mint they are deposited into the coinstore of the destination address. | Enforced via mint. |
Module-level Specification
pragma verify = true;
pragma aborts_if_is_partial;
Function burn
public entry fun burn<CoinType>(account: &signer, amount: u64)
pragma verify = false;
let account_addr = signer::address_of(account);
aborts_if !exists<Capabilities<CoinType>>(account_addr);
let coin_store = global<coin::CoinStore<CoinType>>(account_addr);
let balance = coin_store.coin.value;
// This enforces high-level requirement 3 and high-level requirement 4:
aborts_if !exists<coin::CoinStore<CoinType>>(account_addr);
aborts_if coin_store.frozen;
aborts_if balance < amount;
let addr = type_info::type_of<CoinType>().account_address;
let maybe_supply = global<coin::CoinInfo<CoinType>>(addr).supply;
aborts_if amount == 0;
aborts_if !exists<coin::CoinInfo<CoinType>>(addr);
include coin::CoinSubAbortsIf<CoinType> { amount:amount };
ensures coin::supply<CoinType> == old(coin::supply<CoinType>) - amount;
Function initialize
public entry fun initialize<CoinType>(account: &signer, name: vector<u8>, symbol: vector<u8>, decimals: u8, monitor_supply: bool)
Make sure name and symbol are legal length.
Only the creator of CoinType can initialize.
The ‘name’ and ‘symbol’ should be valid utf8 bytes
The Capabilities
include coin::InitializeInternalSchema<CoinType>;
aborts_if !string::spec_internal_check_utf8(name);
aborts_if !string::spec_internal_check_utf8(symbol);
aborts_if exists<Capabilities<CoinType>>(signer::address_of(account));
// This enforces high-level requirement 1 and high-level requirement 3:
ensures exists<Capabilities<CoinType>>(signer::address_of(account));
Function mint
public entry fun mint<CoinType>(account: &signer, dst_addr: address, amount: u64)
The Capabilitiesdst_addr should not be frozen.
pragma verify = false;
let account_addr = signer::address_of(account);
// This enforces high-level requirement 3:
aborts_if !exists<Capabilities<CoinType>>(account_addr);
let addr = type_info::type_of<CoinType>().account_address;
aborts_if (amount != 0) && !exists<coin::CoinInfo<CoinType>>(addr);
let coin_store = global<coin::CoinStore<CoinType>>(dst_addr);
aborts_if !exists<coin::CoinStore<CoinType>>(dst_addr);
aborts_if coin_store.frozen;
include coin::CoinAddAbortsIf<CoinType>;
ensures coin::supply<CoinType> == old(coin::supply<CoinType>) + amount;
// This enforces high-level requirement 6:
ensures global<coin::CoinStore<CoinType>>(dst_addr).coin.value == old(global<coin::CoinStore<CoinType>>(dst_addr)).coin.value + amount;
Function register
public entry fun register<CoinType>(account: &signer)
An account can only be registered once.
Updating Account.guid_creation_num will not overflow.
pragma verify = false;
let account_addr = signer::address_of(account);
let acc = global<account::Account>(account_addr);
aborts_if !exists<coin::CoinStore<CoinType>>(account_addr) && acc.guid_creation_num + 2 >= account::MAX_GUID_CREATION_NUM;
aborts_if !exists<coin::CoinStore<CoinType>>(account_addr) && acc.guid_creation_num + 2 > MAX_U64;
aborts_if !exists<coin::CoinStore<CoinType>>(account_addr) && !exists<account::Account>(account_addr);
aborts_if !exists<coin::CoinStore<CoinType>>(account_addr) && !type_info::spec_is_struct<CoinType>();
ensures exists<coin::CoinStore<CoinType>>(account_addr);
Function destroy_caps
public entry fun destroy_caps<CoinType>(account: &signer)
let account_addr = signer::address_of(account);
aborts_if !exists<Capabilities<CoinType>>(account_addr);
ensures !exists<Capabilities<CoinType>>(account_addr);
Function remove_caps
public fun remove_caps<CoinType>(account: &signer): (coin::BurnCapability<CoinType>, coin::FreezeCapability<CoinType>, coin::MintCapability<CoinType>)
let account_addr = signer::address_of(account);
aborts_if !exists<Capabilities<CoinType>>(account_addr);
ensures !exists<Capabilities<CoinType>>(account_addr);