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