Skip to content

managed_coin - [mainnet]

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,

use 0x1::coin;
use 0x1::error;
use 0x1::signer;
use 0x1::string;

Constants

Account has no capabilities (burn/mint).

const ENO_CAPABILITIES: u64 = 1;

Resources

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>

Functions

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);
}

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,
});
}

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);
}

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);
}

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);
}

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.RequirementCriticalityImplementationEnforcement
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;

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;

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 should not be under the signer before creating; The Capabilities should be under the signer after creating;

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));

mint

public entry fun mint<CoinType>(account: &signer, dst_addr: address, amount: u64)

The Capabilities should not exist in the signer address. The dst_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;

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);

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);

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);