Module 0x1::coin
This module provides the foundation for typesafe Coins.
- Struct
Coin - Struct
AggregatableCoin - Resource
CoinStore - Resource
SupplyConfig - Resource
CoinInfo - Struct
CoinDeposit - Struct
CoinWithdraw - Struct
Deposit - Struct
Withdraw - Struct
DepositEvent - Struct
WithdrawEvent - Struct
CoinEventHandleDeletion - Struct
CoinStoreDeletion - Struct
PairCreation - Struct
MintCapability - Struct
FreezeCapability - Struct
BurnCapability - Resource
CoinConversionMap - Resource
PairedCoinType - Resource
PairedFungibleAssetRefs - Struct
MintRefReceipt - Struct
TransferRefReceipt - Struct
BurnRefReceipt - Resource
MigrationFlag - Resource
Ghost$supply - Resource
Ghost$aggregate_supply - Constants
- Function
paired_metadata - Function
create_coin_conversion_map - Function
create_pairing - Function
is_apt - Function
create_and_return_paired_metadata_if_not_exist - Function
ensure_paired_metadata - Function
paired_coin - Function
coin_to_fungible_asset - Function
fungible_asset_to_coin - Function
assert_paired_metadata_exists - Function
paired_mint_ref_exists - Function
get_paired_mint_ref - Function
return_paired_mint_ref - Function
paired_transfer_ref_exists - Function
get_paired_transfer_ref - Function
return_paired_transfer_ref - Function
paired_burn_ref_exists - Function
get_paired_burn_copy_ref - Function
get_paired_burn_ref - Function
convert_and_take_paired_burn_ref - Function
return_paired_burn_ref - Function
borrow_paired_burn_ref - Function
allow_supply_upgrades - Function
calculate_amount_to_withdraw - Function
maybe_convert_to_fungible_store - Function
assert_signer_has_permission - Function
migrate_to_fungible_store - Function
migrate_coin_store_to_fungible_store - Function
coin_address - Function
balance - Function
is_balance_at_least - Function
coin_balance - Function
is_coin_initialized - Function
is_coin_store_frozen - Function
is_account_registered - Function
name - Function
symbol - Function
decimals - Function
supply - Function
coin_supply - Function
burn - Function
burn_from - Function
burn_from_for_gas - Function
deposit - Function
deposit_with_signer - Function
deposit_for_gas_fee - Function
destroy_zero - Function
extract - Function
extract_all - Function
freeze_coin_store - Function
unfreeze_coin_store - Function
upgrade_supply - Function
initialize - Function
initialize_with_parallelizable_supply - Function
initialize_internal - Function
merge - Function
mint - Function
register - Function
transfer - Function
value - Function
withdraw - Function
zero - Function
destroy_freeze_cap - Function
destroy_mint_cap - Function
destroy_burn_cap - Function
mint_internal - Function
burn_internal - Specification
- High-level Requirements
- Module-level Specification
- Struct
AggregatableCoin - Function
coin_to_fungible_asset - Function
fungible_asset_to_coin - Function
allow_supply_upgrades - Function
maybe_convert_to_fungible_store - Function
coin_address - Function
balance - Function
is_coin_initialized - Function
is_account_registered - Function
name - Function
symbol - Function
decimals - Function
supply - Function
coin_supply - Function
burn - Function
burn_from - Function
deposit - Function
deposit_for_gas_fee - Function
destroy_zero - Function
extract - Function
extract_all - Function
freeze_coin_store - Function
unfreeze_coin_store - Function
upgrade_supply - Function
initialize - Function
initialize_with_parallelizable_supply - Function
initialize_internal - Function
merge - Function
mint - Function
register - Function
transfer - Function
withdraw - Function
mint_internal - Function
burn_internal
use 0x1::account;
use 0x1::aggregator;
use 0x1::create_signer;
use 0x1::error;
use 0x1::event;
use 0x1::fungible_asset;
use 0x1::guid;
use 0x1::object;
use 0x1::option;
use 0x1::optional_aggregator;
use 0x1::permissioned_signer;
use 0x1::primary_fungible_store;
use 0x1::signer;
use 0x1::string;
use 0x1::system_addresses;
use 0x1::table;
use 0x1::type_info;
use 0x1::vector;
Struct Coin
Core data structures Main structure representing a coin/token in an account’s custody.
struct Coin<CoinType> has store
Fields
-
value: u64 - Amount of coin this address has.
Struct AggregatableCoin
DEPRECATED
#[deprecated]
struct AggregatableCoin<CoinType> has store
Fields
-
value: aggregator::Aggregator - Amount of aggregatable coin this address has.
Resource CoinStore
A holder of a specific coin types and associated event handles. These are kept in a single resource to ensure locality of data.
struct CoinStore<CoinType> has key
Fields
-
coin: coin::Coin<CoinType> -
frozen: bool -
deposit_events: event::EventHandle<coin::DepositEvent> -
withdraw_events: event::EventHandle<coin::WithdrawEvent>
Resource SupplyConfig
Configuration that controls the behavior of total coin supply. If the field is set, coin creators are allowed to upgrade to parallelizable implementations.
#[deprecated]
struct SupplyConfig has key
Fields
-
allow_upgrades: bool
Resource CoinInfo
Information about a specific coin type. Stored on the creator of the coin’s account.
struct CoinInfo<CoinType> has key
Fields
-
name: string::String -
symbol: string::String - Symbol of the coin, usually a shorter version of the name. For example, Singapore Dollar is SGD.
-
decimals: u8 -
Number of decimals used to get its user representation.
For example, if
decimalsequals2, a balance of505coins should be displayed to a user as5.05(505 / 10 ** 2). -
supply: option::Option<optional_aggregator::OptionalAggregator> - Amount of this coin type in existence.
Struct CoinDeposit
Module event emitted when some amount of a coin is deposited into an account.
#[event]
struct CoinDeposit has drop, store
Fields
-
coin_type: string::String -
account: address -
amount: u64
Struct CoinWithdraw
Module event emitted when some amount of a coin is withdrawn from an account.
#[event]
struct CoinWithdraw has drop, store
Fields
-
coin_type: string::String -
account: address -
amount: u64
Struct Deposit
#[event]
#[deprecated]
struct Deposit<CoinType> has drop, store
Fields
-
account: address -
amount: u64
Struct Withdraw
#[event]
#[deprecated]
struct Withdraw<CoinType> has drop, store
Fields
-
account: address -
amount: u64
Struct DepositEvent
Event emitted when some amount of a coin is deposited into an account.
struct DepositEvent has drop, store
Fields
-
amount: u64
Struct WithdrawEvent
Event emitted when some amount of a coin is withdrawn from an account.
struct WithdrawEvent has drop, store
Fields
-
amount: u64
Struct CoinEventHandleDeletion
Module event emitted when the event handles related to coin store is deleted.
Deprecated: replaced with CoinStoreDeletion
#[event]
#[deprecated]
struct CoinEventHandleDeletion has drop, store
Fields
-
event_handle_creation_address: address -
deleted_deposit_event_handle_creation_number: u64 -
deleted_withdraw_event_handle_creation_number: u64
Struct CoinStoreDeletion
Module event emitted when the event handles related to coin store is deleted.
#[event]
struct CoinStoreDeletion has drop, store
Fields
-
coin_type: string::String -
event_handle_creation_address: address -
deleted_deposit_event_handle_creation_number: u64 -
deleted_withdraw_event_handle_creation_number: u64
Struct PairCreation
Module event emitted when a new pair of coin and fungible asset is created.
#[event]
struct PairCreation has drop, store
Fields
-
coin_type: type_info::TypeInfo -
fungible_asset_metadata_address: address
Struct MintCapability
Capability required to mint coins.
struct MintCapability<CoinType> has copy, store
Fields
-
dummy_field: bool
Struct FreezeCapability
Capability required to freeze a coin store.
struct FreezeCapability<CoinType> has copy, store
Fields
-
dummy_field: bool
Struct BurnCapability
Capability required to burn coins.
struct BurnCapability<CoinType> has copy, store
Fields
-
dummy_field: bool
Resource CoinConversionMap
The mapping between coin and fungible asset.
struct CoinConversionMap has key
Fields
-
coin_to_fungible_asset_map: table::Table<type_info::TypeInfo, object::Object<fungible_asset::Metadata>>
Resource PairedCoinType
The paired coin type info stored in fungible asset metadata object.
#[resource_group_member(#[group = 0x1::object::ObjectGroup])]
struct PairedCoinType has key
Fields
-
type: type_info::TypeInfo
Resource PairedFungibleAssetRefs
The refs of the paired fungible asset.
#[resource_group_member(#[group = 0x1::object::ObjectGroup])]
struct PairedFungibleAssetRefs has key
Fields
-
mint_ref_opt: option::Option<fungible_asset::MintRef> -
transfer_ref_opt: option::Option<fungible_asset::TransferRef> -
burn_ref_opt: option::Option<fungible_asset::BurnRef>
Struct MintRefReceipt
The hot potato receipt for flash borrowing MintRef.
struct MintRefReceipt
Fields
-
metadata: object::Object<fungible_asset::Metadata>
Struct TransferRefReceipt
The hot potato receipt for flash borrowing TransferRef.
struct TransferRefReceipt
Fields
-
metadata: object::Object<fungible_asset::Metadata>
Struct BurnRefReceipt
The hot potato receipt for flash borrowing BurnRef.
struct BurnRefReceipt
Fields
-
metadata: object::Object<fungible_asset::Metadata>
Resource MigrationFlag
The flag the existence of which indicates the primary fungible store is created by the migration from CoinStore.
#[resource_group_member(#[group = 0x1::object::ObjectGroup])]
#[deprecated]
struct MigrationFlag has key
Fields
-
dummy_field: bool
Resource Ghost$supply
struct Ghost$supply<CoinType> has copy, drop, store, key
Fields
-
v: num
Resource Ghost$aggregate_supply
struct Ghost$aggregate_supply<CoinType> has copy, drop, store, key
Fields
-
v: num
Constants
Maximum possible aggregatable coin value.
const MAX_U64: u128 = 18446744073709551615;
Not enough coins to complete transaction
const EINSUFFICIENT_BALANCE: u64 = 6;
const MAX_DECIMALS: u8 = 32;
The value of aggregatable coin used for transaction fees redistribution does not fit in u64.
const EAGGREGATABLE_COIN_VALUE_TOO_LARGE: u64 = 14;
APT pairing is not eanbled yet.
const EAPT_PAIRING_IS_NOT_ENABLED: u64 = 28;
The BurnRef does not exist.
const EBURN_REF_NOT_FOUND: u64 = 25;
The BurnRefReceipt does not match the BurnRef to be returned.
const EBURN_REF_RECEIPT_MISMATCH: u64 = 24;
The coin converison map is not created yet.
const ECOIN_CONVERSION_MAP_NOT_FOUND: u64 = 27;
The decimals of the coin is too large.
const ECOIN_DECIMALS_TOO_LARGE: u64 = 29;
Address of account which is used to initialize a coin CoinType doesn’t match the deployer of module
const ECOIN_INFO_ADDRESS_MISMATCH: u64 = 1;
CoinType is already initialized as a coin
const ECOIN_INFO_ALREADY_PUBLISHED: u64 = 2;
CoinType hasn’t been initialized as a coin
const ECOIN_INFO_NOT_PUBLISHED: u64 = 3;
Name of the coin is too long
const ECOIN_NAME_TOO_LONG: u64 = 12;
Deprecated. Account already has CoinStore registered for CoinType
const ECOIN_STORE_ALREADY_PUBLISHED: u64 = 4;
Account hasn’t registered CoinStore for CoinType
const ECOIN_STORE_NOT_PUBLISHED: u64 = 5;
Cannot upgrade the total supply of coins to different implementation.
const ECOIN_SUPPLY_UPGRADE_NOT_SUPPORTED: u64 = 11;
Symbol of the coin is too long
const ECOIN_SYMBOL_TOO_LONG: u64 = 13;
The feature of migration from coin to fungible asset is not enabled.
const ECOIN_TO_FUNGIBLE_ASSET_FEATURE_NOT_ENABLED: u64 = 18;
The coin type from the map does not match the calling function type argument.
const ECOIN_TYPE_MISMATCH: u64 = 17;
Cannot destroy non-zero coins
const EDESTRUCTION_OF_NONZERO_TOKEN: u64 = 7;
CoinStore is frozen. Coins cannot be deposited or withdrawn
const EFROZEN: u64 = 10;
The migration process from coin to fungible asset is not enabled yet.
const EMIGRATION_FRAMEWORK_NOT_ENABLED: u64 = 26;
The MintRef does not exist.
const EMINT_REF_NOT_FOUND: u64 = 21;
The MintRefReceipt does not match the MintRef to be returned.
const EMINT_REF_RECEIPT_MISMATCH: u64 = 20;
Error regarding paired coin type of the fungible asset metadata.
const EPAIRED_COIN: u64 = 15;
Error regarding paired fungible asset metadata of a coin type.
const EPAIRED_FUNGIBLE_ASSET: u64 = 16;
PairedFungibleAssetRefs resource does not exist.
const EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND: u64 = 19;
The TransferRef does not exist.
const ETRANSFER_REF_NOT_FOUND: u64 = 23;
The TransferRefReceipt does not match the TransferRef to be returned.
const ETRANSFER_REF_RECEIPT_MISMATCH: u64 = 22;
const MAX_COIN_NAME_LENGTH: u64 = 32;
const MAX_COIN_SYMBOL_LENGTH: u64 = 32;
Function paired_metadata
Get the paired fungible asset metadata object of a coin type. If not exist, return option::none().
#[view]
public fun paired_metadata<CoinType>(): option::Option<object::Object<fungible_asset::Metadata>>
Implementation
public fun paired_metadata<CoinType>(): Option<Object<Metadata>> acquires CoinConversionMap {
if (exists<CoinConversionMap>(@aptos_framework)) {
let map =
&borrow_global<CoinConversionMap>(@aptos_framework).coin_to_fungible_asset_map;
let type = type_info::type_of<CoinType>();
if (map.contains(type)) {
return option::some(*map.borrow(type))
}
};
option::none()
}
Function create_coin_conversion_map
public entry fun create_coin_conversion_map(aptos_framework: &signer)
Implementation
public entry fun create_coin_conversion_map(aptos_framework: &signer) {
system_addresses::assert_aptos_framework(aptos_framework);
if (!exists<CoinConversionMap>(@aptos_framework)) {
move_to(
aptos_framework,
CoinConversionMap { coin_to_fungible_asset_map: table::new() }
)
};
}
Function create_pairing
Create APT pairing by passing AptosCoin.
public entry fun create_pairing<CoinType>(aptos_framework: &signer)
Implementation
public entry fun create_pairing<CoinType>(
aptos_framework: &signer
) acquires CoinConversionMap, CoinInfo {
system_addresses::assert_aptos_framework(aptos_framework);
create_and_return_paired_metadata_if_not_exist<CoinType>(true);
}
Function is_apt
fun is_apt<CoinType>(): bool
Implementation
inline fun is_apt<CoinType>(): bool {
type_info::type_name<CoinType>() == string::utf8(b"0x1::aptos_coin::AptosCoin")
}
Function create_and_return_paired_metadata_if_not_exist
fun create_and_return_paired_metadata_if_not_exist<CoinType>(allow_apt_creation: bool): object::Object<fungible_asset::Metadata>
Implementation
inline fun create_and_return_paired_metadata_if_not_exist<CoinType>(
allow_apt_creation: bool
): Object<Metadata> {
assert!(
exists<CoinConversionMap>(@aptos_framework),
error::not_found(ECOIN_CONVERSION_MAP_NOT_FOUND)
);
let map = borrow_global_mut<CoinConversionMap>(@aptos_framework);
let type = type_info::type_of<CoinType>();
if (!map.coin_to_fungible_asset_map.contains(type)) {
let is_apt = is_apt<CoinType>();
assert!(
!is_apt || allow_apt_creation,
error::invalid_state(EAPT_PAIRING_IS_NOT_ENABLED)
);
let metadata_object_cref =
if (is_apt) {
object::create_sticky_object_at_address(
@aptos_framework, @aptos_fungible_asset
)
} else {
object::create_named_object(
&create_signer::create_signer(@aptos_fungible_asset),
*type_info::type_name<CoinType>().bytes()
)
};
primary_fungible_store::create_primary_store_enabled_fungible_asset(
&metadata_object_cref,
option::none(),
name<CoinType>(),
symbol<CoinType>(),
decimals<CoinType>(),
string::utf8(b""),
string::utf8(b"")
);
let metadata_object_signer = &metadata_object_cref.generate_signer();
let type = type_info::type_of<CoinType>();
move_to(metadata_object_signer, PairedCoinType { type });
let metadata_obj = metadata_object_cref.object_from_constructor_ref();
map.coin_to_fungible_asset_map.add(type, metadata_obj);
event::emit(
PairCreation {
coin_type: type,
fungible_asset_metadata_address: metadata_obj.object_address()
}
);
// Generates all three refs
let mint_ref = fungible_asset::generate_mint_ref(&metadata_object_cref);
let transfer_ref =
fungible_asset::generate_transfer_ref(&metadata_object_cref);
let burn_ref = fungible_asset::generate_burn_ref(&metadata_object_cref);
move_to(
metadata_object_signer,
PairedFungibleAssetRefs {
mint_ref_opt: option::some(mint_ref),
transfer_ref_opt: option::some(transfer_ref),
burn_ref_opt: option::some(burn_ref)
}
);
};
*map.coin_to_fungible_asset_map.borrow(type)
}
Function ensure_paired_metadata
Get the paired fungible asset metadata object of a coin type, create if not exist.
public(friend) fun ensure_paired_metadata<CoinType>(): object::Object<fungible_asset::Metadata>
Implementation
public(friend) fun ensure_paired_metadata<CoinType>(): Object<Metadata> acquires CoinConversionMap, CoinInfo {
create_and_return_paired_metadata_if_not_exist<CoinType>(false)
}
Function paired_coin
Get the paired coin type of a fungible asset metadata object.
#[view]
public fun paired_coin(metadata: object::Object<fungible_asset::Metadata>): option::Option<type_info::TypeInfo>
Implementation
public fun paired_coin(metadata: Object<Metadata>): Option<TypeInfo> acquires PairedCoinType {
let metadata_addr = metadata.object_address();
if (exists<PairedCoinType>(metadata_addr)) {
option::some(borrow_global<PairedCoinType>(metadata_addr).type)
} else {
option::none()
}
}
Function coin_to_fungible_asset
Conversion from coin to fungible asset
public fun coin_to_fungible_asset<CoinType>(coin: coin::Coin<CoinType>): fungible_asset::FungibleAsset
Implementation
public fun coin_to_fungible_asset<CoinType>(
coin: Coin<CoinType>
): FungibleAsset acquires CoinConversionMap, CoinInfo {
let metadata = ensure_paired_metadata<CoinType>();
let amount = burn_internal(coin);
fungible_asset::mint_internal(metadata, amount)
}
Function fungible_asset_to_coin
Conversion from fungible asset to coin. Not public to push the migration to FA.
fun fungible_asset_to_coin<CoinType>(fungible_asset: fungible_asset::FungibleAsset): coin::Coin<CoinType>
Implementation
fun fungible_asset_to_coin<CoinType>(
fungible_asset: FungibleAsset
): Coin<CoinType> acquires CoinInfo, PairedCoinType {
let metadata_addr =
fungible_asset.asset_metadata().object_address();
assert!(
object::object_exists<PairedCoinType>(metadata_addr),
error::not_found(EPAIRED_COIN)
);
let coin_type_info = borrow_global<PairedCoinType>(metadata_addr).type;
assert!(
coin_type_info == type_info::type_of<CoinType>(),
error::invalid_argument(ECOIN_TYPE_MISMATCH)
);
let amount = fungible_asset.burn_internal();
mint_internal<CoinType>(amount)
}
Function assert_paired_metadata_exists
fun assert_paired_metadata_exists<CoinType>(): object::Object<fungible_asset::Metadata>
Implementation
inline fun assert_paired_metadata_exists<CoinType>(): Object<Metadata> {
let metadata_opt = paired_metadata<CoinType>();
assert!(
metadata_opt.is_some(), error::not_found(EPAIRED_FUNGIBLE_ASSET)
);
metadata_opt.destroy_some()
}
Function paired_mint_ref_exists
Check whether MintRef has not been taken.
#[view]
public fun paired_mint_ref_exists<CoinType>(): bool
Implementation
public fun paired_mint_ref_exists<CoinType>(): bool acquires CoinConversionMap, PairedFungibleAssetRefs {
let metadata = assert_paired_metadata_exists<CoinType>();
let metadata_addr = metadata.object_address();
assert!(
exists<PairedFungibleAssetRefs>(metadata_addr),
error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND)
);
borrow_global<PairedFungibleAssetRefs>(metadata_addr).mint_ref_opt.is_some()
}
Function get_paired_mint_ref
Get the MintRef of paired fungible asset of a coin type from MintCapability.
public fun get_paired_mint_ref<CoinType>(_: &coin::MintCapability<CoinType>): (fungible_asset::MintRef, coin::MintRefReceipt)
Implementation
public fun get_paired_mint_ref<CoinType>(
_: &MintCapability<CoinType>
): (MintRef, MintRefReceipt) acquires CoinConversionMap, PairedFungibleAssetRefs {
let metadata = assert_paired_metadata_exists<CoinType>();
let metadata_addr = metadata.object_address();
assert!(
exists<PairedFungibleAssetRefs>(metadata_addr),
error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND)
);
let mint_ref_opt =
&mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).mint_ref_opt;
assert!(mint_ref_opt.is_some(), error::not_found(EMINT_REF_NOT_FOUND));
(mint_ref_opt.extract(), MintRefReceipt { metadata })
}
Function return_paired_mint_ref
Return the MintRef with the hot potato receipt.
public fun return_paired_mint_ref(mint_ref: fungible_asset::MintRef, receipt: coin::MintRefReceipt)
Implementation
public fun return_paired_mint_ref(
mint_ref: MintRef, receipt: MintRefReceipt
) acquires PairedFungibleAssetRefs {
let MintRefReceipt { metadata } = receipt;
assert!(
mint_ref.mint_ref_metadata() == metadata,
error::invalid_argument(EMINT_REF_RECEIPT_MISMATCH)
);
let metadata_addr = metadata.object_address();
let mint_ref_opt =
&mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).mint_ref_opt;
mint_ref_opt.fill(mint_ref);
}
Function paired_transfer_ref_exists
Check whether TransferRef still exists.
#[view]
public fun paired_transfer_ref_exists<CoinType>(): bool
Implementation
public fun paired_transfer_ref_exists<CoinType>(): bool acquires CoinConversionMap, PairedFungibleAssetRefs {
let metadata = assert_paired_metadata_exists<CoinType>();
let metadata_addr = metadata.object_address();
assert!(
exists<PairedFungibleAssetRefs>(metadata_addr),
error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND)
);
borrow_global<PairedFungibleAssetRefs>(metadata_addr).transfer_ref_opt.is_some()
}
Function get_paired_transfer_ref
Get the TransferRef of paired fungible asset of a coin type from FreezeCapability.
public fun get_paired_transfer_ref<CoinType>(_: &coin::FreezeCapability<CoinType>): (fungible_asset::TransferRef, coin::TransferRefReceipt)
Implementation
public fun get_paired_transfer_ref<CoinType>(
_: &FreezeCapability<CoinType>
): (TransferRef, TransferRefReceipt) acquires CoinConversionMap, PairedFungibleAssetRefs {
let metadata = assert_paired_metadata_exists<CoinType>();
let metadata_addr = metadata.object_address();
assert!(
exists<PairedFungibleAssetRefs>(metadata_addr),
error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND)
);
let transfer_ref_opt =
&mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).transfer_ref_opt;
assert!(
transfer_ref_opt.is_some(),
error::not_found(ETRANSFER_REF_NOT_FOUND)
);
(transfer_ref_opt.extract(), TransferRefReceipt { metadata })
}
Function return_paired_transfer_ref
Return the TransferRef with the hot potato receipt.
public fun return_paired_transfer_ref(transfer_ref: fungible_asset::TransferRef, receipt: coin::TransferRefReceipt)
Implementation
public fun return_paired_transfer_ref(
transfer_ref: TransferRef, receipt: TransferRefReceipt
) acquires PairedFungibleAssetRefs {
let TransferRefReceipt { metadata } = receipt;
assert!(
transfer_ref.transfer_ref_metadata() == metadata,
error::invalid_argument(ETRANSFER_REF_RECEIPT_MISMATCH)
);
let metadata_addr = metadata.object_address();
let transfer_ref_opt =
&mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).transfer_ref_opt;
transfer_ref_opt.fill(transfer_ref);
}
Function paired_burn_ref_exists
Check whether BurnRef has not been taken.
#[view]
public fun paired_burn_ref_exists<CoinType>(): bool
Implementation
public fun paired_burn_ref_exists<CoinType>(): bool acquires CoinConversionMap, PairedFungibleAssetRefs {
let metadata = assert_paired_metadata_exists<CoinType>();
let metadata_addr = metadata.object_address();
assert!(
exists<PairedFungibleAssetRefs>(metadata_addr),
error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND)
);
borrow_global<PairedFungibleAssetRefs>(metadata_addr).burn_ref_opt.is_some()
}
Function get_paired_burn_copy_ref
public(friend) fun get_paired_burn_copy_ref<CoinType>(burn_cap: &coin::BurnCapability<CoinType>): fungible_asset::BurnRef
Implementation
public(friend) fun get_paired_burn_copy_ref<CoinType>(
burn_cap: &BurnCapability<CoinType>
): BurnRef acquires CoinConversionMap, PairedFungibleAssetRefs {
let burn_ref = borrow_paired_burn_ref(burn_cap);
burn_ref.generate_burn_copy_ref()
}
Function get_paired_burn_ref
Get the BurnRef of paired fungible asset of a coin type from BurnCapability.
public fun get_paired_burn_ref<CoinType>(_: &coin::BurnCapability<CoinType>): (fungible_asset::BurnRef, coin::BurnRefReceipt)
Implementation
public fun get_paired_burn_ref<CoinType>(
_: &BurnCapability<CoinType>
): (BurnRef, BurnRefReceipt) acquires CoinConversionMap, PairedFungibleAssetRefs {
let metadata = assert_paired_metadata_exists<CoinType>();
let metadata_addr = metadata.object_address();
assert!(
exists<PairedFungibleAssetRefs>(metadata_addr),
error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND)
);
let burn_ref_opt =
&mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).burn_ref_opt;
assert!(burn_ref_opt.is_some(), error::not_found(EBURN_REF_NOT_FOUND));
(burn_ref_opt.extract(), BurnRefReceipt { metadata })
}
Function convert_and_take_paired_burn_ref
public fun convert_and_take_paired_burn_ref<CoinType>(burn_cap: coin::BurnCapability<CoinType>): fungible_asset::BurnRef
Implementation
public fun convert_and_take_paired_burn_ref<CoinType>(
burn_cap: BurnCapability<CoinType>
): BurnRef acquires CoinConversionMap, PairedFungibleAssetRefs {
destroy_burn_cap(burn_cap);
let metadata = assert_paired_metadata_exists<CoinType>();
let metadata_addr = metadata.object_address();
assert!(
exists<PairedFungibleAssetRefs>(metadata_addr),
error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND)
);
let burn_ref_opt =
&mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).burn_ref_opt;
assert!(burn_ref_opt.is_some(), error::not_found(EBURN_REF_NOT_FOUND));
burn_ref_opt.extract()
}
Function return_paired_burn_ref
Return the BurnRef with the hot potato receipt.
public fun return_paired_burn_ref(burn_ref: fungible_asset::BurnRef, receipt: coin::BurnRefReceipt)
Implementation
public fun return_paired_burn_ref(
burn_ref: BurnRef, receipt: BurnRefReceipt
) acquires PairedFungibleAssetRefs {
let BurnRefReceipt { metadata } = receipt;
assert!(
burn_ref.burn_ref_metadata() == metadata,
error::invalid_argument(EBURN_REF_RECEIPT_MISMATCH)
);
let metadata_addr = metadata.object_address();
let burn_ref_opt =
&mut borrow_global_mut<PairedFungibleAssetRefs>(metadata_addr).burn_ref_opt;
burn_ref_opt.fill(burn_ref);
}
Function borrow_paired_burn_ref
fun borrow_paired_burn_ref<CoinType>(_: &coin::BurnCapability<CoinType>): &fungible_asset::BurnRef
Implementation
inline fun borrow_paired_burn_ref<CoinType>(
_: &BurnCapability<CoinType>
): &BurnRef {
let metadata = assert_paired_metadata_exists<CoinType>();
let metadata_addr = metadata.object_address();
assert!(
exists<PairedFungibleAssetRefs>(metadata_addr),
error::internal(EPAIRED_FUNGIBLE_ASSET_REFS_NOT_FOUND)
);
let burn_ref_opt =
&borrow_global<PairedFungibleAssetRefs>(metadata_addr).burn_ref_opt;
assert!(burn_ref_opt.is_some(), error::not_found(EBURN_REF_NOT_FOUND));
burn_ref_opt.borrow()
}
Function allow_supply_upgrades
This should be called by on-chain governance to update the config and allow or disallow upgradability of total supply.
public fun allow_supply_upgrades(_aptos_framework: &signer, _allowed: bool)
Implementation
public fun allow_supply_upgrades(
_aptos_framework: &signer, _allowed: bool
) {
abort error::invalid_state(ECOIN_SUPPLY_UPGRADE_NOT_SUPPORTED)
}
Function calculate_amount_to_withdraw
fun calculate_amount_to_withdraw<CoinType>(account_addr: address, amount: u64): (u64, u64)
Implementation
inline fun calculate_amount_to_withdraw<CoinType>(
account_addr: address, amount: u64
): (u64, u64) {
let coin_balance = coin_balance<CoinType>(account_addr);
if (coin_balance >= amount) {
(amount, 0)
} else {
let metadata = paired_metadata<CoinType>();
if (metadata.is_some()
&& primary_fungible_store::primary_store_exists(
account_addr, metadata.destroy_some()
))
(coin_balance, amount - coin_balance)
else abort error::invalid_argument(EINSUFFICIENT_BALANCE)
}
}
Function maybe_convert_to_fungible_store
fun maybe_convert_to_fungible_store<CoinType>(account: address)
Implementation
fun maybe_convert_to_fungible_store<CoinType>(
account: address
) acquires CoinStore, CoinConversionMap, CoinInfo {
if (exists<CoinStore<CoinType>>(account)) {
let CoinStore<CoinType> { coin, frozen, deposit_events, withdraw_events } =
move_from<CoinStore<CoinType>>(account);
if (is_coin_initialized<CoinType>() && coin.value > 0) {
let metadata = ensure_paired_metadata<CoinType>();
let store =
primary_fungible_store::ensure_primary_store_exists(
account, metadata
);
event::emit(
CoinStoreDeletion {
coin_type: type_info::type_name<CoinType>(),
event_handle_creation_address: guid::creator_address(
event::guid(&deposit_events)
),
deleted_deposit_event_handle_creation_number: guid::creation_num(
event::guid(&deposit_events)
),
deleted_withdraw_event_handle_creation_number: guid::creation_num(
event::guid(&withdraw_events)
)
}
);
if (coin.value == 0) {
destroy_zero(coin);
} else {
fungible_asset::unchecked_deposit_with_no_events(
store.object_address(),
coin_to_fungible_asset(coin)
);
};
// Note:
// It is possible the primary fungible store may already exist before this function call.
// In this case, if the account owns a frozen CoinStore and an unfrozen primary fungible store, this
// function would convert and deposit the rest coin into the primary store and freeze it to make the
// `frozen` semantic as consistent as possible.
if (frozen != fungible_asset::is_frozen(store)) {
fungible_asset::set_frozen_flag_internal(store, frozen);
}
} else {
destroy_zero(coin);
};
event::destroy_handle(deposit_events);
event::destroy_handle(withdraw_events);
};
}
Function assert_signer_has_permission
fun assert_signer_has_permission<CoinType>(account: &signer)
Implementation
inline fun assert_signer_has_permission<CoinType>(account: &signer) {
if (permissioned_signer::is_permissioned_signer(account)) {
fungible_asset::withdraw_permission_check_by_address(
account,
primary_fungible_store::primary_store_address(
signer::address_of(account),
ensure_paired_metadata<CoinType>()
),
0
);
}
}
Function migrate_to_fungible_store
Voluntarily migrate to fungible store for CoinType if not yet.
public entry fun migrate_to_fungible_store<CoinType>(account: &signer)
Implementation
public entry fun migrate_to_fungible_store<CoinType>(
account: &signer
) acquires CoinStore, CoinConversionMap, CoinInfo {
let account_addr = signer::address_of(account);
assert_signer_has_permission<CoinType>(account);
maybe_convert_to_fungible_store<CoinType>(account_addr);
}
Function migrate_coin_store_to_fungible_store
Migrate to fungible store for CoinType if not yet.
public entry fun migrate_coin_store_to_fungible_store<CoinType>(accounts: vector<address>)
Implementation
public entry fun migrate_coin_store_to_fungible_store<CoinType>(
accounts: vector<address>
) acquires CoinStore, CoinConversionMap, CoinInfo {
accounts.for_each(|account| {
maybe_convert_to_fungible_store<CoinType>(account);
});
}
Function coin_address
A helper function that returns the address of CoinType.
fun coin_address<CoinType>(): address
Implementation
fun coin_address<CoinType>(): address {
let type_info = type_info::type_of<CoinType>();
type_info.account_address()
}
Function balance
Returns the balance of owner for provided CoinType and its paired FA if exists.
#[view]
public fun balance<CoinType>(owner: address): u64
Implementation
public fun balance<CoinType>(owner: address): u64 acquires CoinConversionMap, CoinStore {
let paired_metadata = paired_metadata<CoinType>();
coin_balance<CoinType>(owner)
+ if (paired_metadata.is_some()) {
primary_fungible_store::balance(
owner, paired_metadata.extract()
)
} else { 0 }
}
Function is_balance_at_least
Returns whether the balance of owner for provided CoinType and its paired FA is >= amount.
#[view]
public fun is_balance_at_least<CoinType>(owner: address, amount: u64): bool
Implementation
public fun is_balance_at_least<CoinType>(
owner: address, amount: u64
): bool acquires CoinConversionMap, CoinStore {
let coin_balance = coin_balance<CoinType>(owner);
if (coin_balance >= amount) {
return true
};
let paired_metadata = paired_metadata<CoinType>();
let left_amount = amount - coin_balance;
if (paired_metadata.is_some()) {
primary_fungible_store::is_balance_at_least(
owner,
paired_metadata.extract(),
left_amount
)
} else { false }
}
Function coin_balance
fun coin_balance<CoinType>(owner: address): u64
Implementation
inline fun coin_balance<CoinType>(owner: address): u64 {
if (exists<CoinStore<CoinType>>(owner)) {
borrow_global<CoinStore<CoinType>>(owner).coin.value
} else { 0 }
}
Function is_coin_initialized
Returns true if the type CoinType is an initialized coin.
#[view]
public fun is_coin_initialized<CoinType>(): bool
Implementation
public fun is_coin_initialized<CoinType>(): bool {
exists<CoinInfo<CoinType>>(coin_address<CoinType>())
}
Function is_coin_store_frozen
Returns true is account_addr has frozen the CoinStore or if it’s not registered at all
#[view]
public fun is_coin_store_frozen<CoinType>(account_addr: address): bool
Implementation
public fun is_coin_store_frozen<CoinType>(account_addr: address): bool acquires CoinStore {
if (!is_account_registered<CoinType>(account_addr)) {
return true
};
let coin_store = borrow_global<CoinStore<CoinType>>(account_addr);
coin_store.frozen
}
Function is_account_registered
Returns true if account_addr is registered to receive CoinType.
#[view]
public fun is_account_registered<CoinType>(_account_addr: address): bool
Implementation
public fun is_account_registered<CoinType>(_account_addr: address): bool {
assert!(
is_coin_initialized<CoinType>(),
error::invalid_argument(ECOIN_INFO_NOT_PUBLISHED)
);
true
}
Function name
Returns the name of the coin.
#[view]
public fun name<CoinType>(): string::String
Implementation
public fun name<CoinType>(): string::String acquires CoinInfo {
borrow_global<CoinInfo<CoinType>>(coin_address<CoinType>()).name
}
Function symbol
Returns the symbol of the coin, usually a shorter version of the name.
#[view]
public fun symbol<CoinType>(): string::String
Implementation
public fun symbol<CoinType>(): string::String acquires CoinInfo {
borrow_global<CoinInfo<CoinType>>(coin_address<CoinType>()).symbol
}
Function decimals
Returns the number of decimals used to get its user representation.
For example, if decimals equals 2, a balance of 505 coins should
be displayed to a user as 5.05 (505 / 10 ** 2).
#[view]
public fun decimals<CoinType>(): u8
Implementation
public fun decimals<CoinType>(): u8 acquires CoinInfo {
borrow_global<CoinInfo<CoinType>>(coin_address<CoinType>()).decimals
}
Function supply
Returns the amount of coin in existence.
#[view]
public fun supply<CoinType>(): option::Option<u128>
Implementation
public fun supply<CoinType>(): Option<u128> acquires CoinInfo, CoinConversionMap {
let coin_supply = coin_supply<CoinType>();
let metadata = paired_metadata<CoinType>();
if (metadata.is_some()) {
let fungible_asset_supply =
fungible_asset::supply(metadata.extract());
if (coin_supply.is_some()) {
let supply = coin_supply.borrow_mut();
*supply += fungible_asset_supply.destroy_some();
};
};
coin_supply
}
Function coin_supply
Returns the amount of coin in existence.
#[view]
public fun coin_supply<CoinType>(): option::Option<u128>
Implementation
public fun coin_supply<CoinType>(): Option<u128> acquires CoinInfo {
let maybe_supply =
&borrow_global<CoinInfo<CoinType>>(coin_address<CoinType>()).supply;
if (maybe_supply.is_some()) {
// We do track supply, in this case read from optional aggregator.
let supply = maybe_supply.borrow();
let value = optional_aggregator::read(supply);
option::some(value)
} else {
option::none()
}
}
Function burn
Burn coin with capability.
The capability _cap should be passed as a reference to BurnCapability<CoinType>.
public fun burn<CoinType>(coin: coin::Coin<CoinType>, _cap: &coin::BurnCapability<CoinType>)
Implementation
public fun burn<CoinType>(
coin: Coin<CoinType>, _cap: &BurnCapability<CoinType>
) acquires CoinInfo {
burn_internal(coin);
}
Function burn_from
Burn coin from the specified account with capability.
The capability burn_cap should be passed as a reference to BurnCapability<CoinType>.
This function shouldn’t fail as it’s called as part of transaction fee burning.
Note: This bypasses CoinStore::frozen – coins within a frozen CoinStore can be burned.
public fun burn_from<CoinType>(account_addr: address, amount: u64, burn_cap: &coin::BurnCapability<CoinType>)
Implementation
public fun burn_from<CoinType>(
account_addr: address, amount: u64, burn_cap: &BurnCapability<CoinType>
) acquires CoinInfo, CoinConversionMap, PairedFungibleAssetRefs {
// Skip burning if amount is zero. This shouldn't error out as it's called as part of transaction fee burning.
if (amount == 0) { return };
borrow_paired_burn_ref(burn_cap).burn_from(primary_fungible_store::primary_store(
account_addr, ensure_paired_metadata<CoinType>()
), amount);
}
Function burn_from_for_gas
public(friend) fun burn_from_for_gas<CoinType>(account_addr: address, amount: u64, burn_cap: &coin::BurnCapability<CoinType>)
Implementation
public(friend) fun burn_from_for_gas<CoinType>(
account_addr: address, amount: u64, burn_cap: &BurnCapability<CoinType>
) acquires CoinInfo, CoinConversionMap, PairedFungibleAssetRefs {
// Skip burning if amount is zero. This shouldn't error out as it's called as part of transaction fee burning.
if (amount == 0) { return };
borrow_paired_burn_ref(burn_cap).address_burn_from_for_gas(primary_fungible_store::primary_store_address(
account_addr, ensure_paired_metadata<CoinType>()
), amount);
}
Function deposit
Deposit the coin balance into the recipient’s account and emit an event.
public fun deposit<CoinType>(account_addr: address, coin: coin::Coin<CoinType>)
Implementation
public fun deposit<CoinType>(
account_addr: address, coin: Coin<CoinType>
) acquires CoinConversionMap, CoinInfo {
primary_fungible_store::deposit(account_addr, coin_to_fungible_asset(coin));
}
Function deposit_with_signer
public fun deposit_with_signer<CoinType>(account: &signer, coin: coin::Coin<CoinType>)
Implementation
public fun deposit_with_signer<CoinType>(
account: &signer, coin: Coin<CoinType>
) acquires CoinConversionMap, CoinInfo {
let metadata = ensure_paired_metadata<CoinType>();
let account_address = signer::address_of(account);
fungible_asset::refill_permission(
account,
coin.value,
primary_fungible_store::primary_store_address_inlined(
account_address, metadata
)
);
deposit(account_address, coin);
}
Function deposit_for_gas_fee
Deposit the coin balance into the recipient’s account without checking if the account is frozen. This is for internal use only and doesn’t emit an DepositEvent.
public(friend) fun deposit_for_gas_fee<CoinType>(account_addr: address, coin: coin::Coin<CoinType>)
Implementation
public(friend) fun deposit_for_gas_fee<CoinType>(
account_addr: address, coin: Coin<CoinType>
) acquires CoinConversionMap, CoinInfo {
let fa = coin_to_fungible_asset(coin);
let metadata = fa.asset_metadata();
let store =
primary_fungible_store::ensure_primary_store_exists(account_addr, metadata);
fungible_asset::unchecked_deposit_with_no_events(
store.object_address(), fa
);
}
Function destroy_zero
Destroys a zero-value coin. Calls will fail if the value in the passed-in token is non-zero
so it is impossible to “burn” any non-zero amount of Coin without having
a BurnCapability for the specific CoinType.
public fun destroy_zero<CoinType>(zero_coin: coin::Coin<CoinType>)
Implementation
public fun destroy_zero<CoinType>(zero_coin: Coin<CoinType>) {
spec {
update supply<CoinType> = supply<CoinType> - zero_coin.value;
};
let Coin { value } = zero_coin;
assert!(value == 0, error::invalid_argument(EDESTRUCTION_OF_NONZERO_TOKEN))
}
Function extract
Extracts amount from the passed-in coin, where the original token is modified in place.
public fun extract<CoinType>(coin: &mut coin::Coin<CoinType>, amount: u64): coin::Coin<CoinType>
Implementation
public fun extract<CoinType>(coin: &mut Coin<CoinType>, amount: u64): Coin<CoinType> {
assert!(coin.value >= amount, error::invalid_argument(EINSUFFICIENT_BALANCE));
spec {
update supply<CoinType> = supply<CoinType> - amount;
};
coin.value -= amount;
spec {
update supply<CoinType> = supply<CoinType> + amount;
};
Coin { value: amount }
}
Function extract_all
Extracts the entire amount from the passed-in coin, where the original token is modified in place.
public fun extract_all<CoinType>(coin: &mut coin::Coin<CoinType>): coin::Coin<CoinType>
Implementation
public fun extract_all<CoinType>(coin: &mut Coin<CoinType>): Coin<CoinType> {
let total_value = coin.value;
spec {
update supply<CoinType> = supply<CoinType> - coin.value;
};
coin.value = 0;
spec {
update supply<CoinType> = supply<CoinType> + total_value;
};
Coin { value: total_value }
}
Function freeze_coin_store
Freeze a CoinStore to prevent transfers
#[legacy_entry_fun]
public entry fun freeze_coin_store<CoinType>(account_addr: address, _freeze_cap: &coin::FreezeCapability<CoinType>)
Implementation
public entry fun freeze_coin_store<CoinType>(
account_addr: address, _freeze_cap: &FreezeCapability<CoinType>
) acquires CoinStore {
let coin_store = borrow_global_mut<CoinStore<CoinType>>(account_addr);
coin_store.frozen = true;
}
Function unfreeze_coin_store
Unfreeze a CoinStore to allow transfers
#[legacy_entry_fun]
public entry fun unfreeze_coin_store<CoinType>(account_addr: address, _freeze_cap: &coin::FreezeCapability<CoinType>)
Implementation
public entry fun unfreeze_coin_store<CoinType>(
account_addr: address, _freeze_cap: &FreezeCapability<CoinType>
) acquires CoinStore {
let coin_store = borrow_global_mut<CoinStore<CoinType>>(account_addr);
coin_store.frozen = false;
}
Function upgrade_supply
Upgrade total supply to use a parallelizable implementation if it is available.
public entry fun upgrade_supply<CoinType>(_account: &signer)
Implementation
public entry fun upgrade_supply<CoinType>(_account: &signer) {
abort error::invalid_state(ECOIN_SUPPLY_UPGRADE_NOT_SUPPORTED)
}
Function initialize
Creates a new Coin with given CoinType and returns minting/freezing/burning capabilities.
The given signer also becomes the account hosting the information about the coin
(name, supply, etc.). Supply is initialized as non-parallelizable integer.
public fun initialize<CoinType>(account: &signer, name: string::String, symbol: string::String, decimals: u8, monitor_supply: bool): (coin::BurnCapability<CoinType>, coin::FreezeCapability<CoinType>, coin::MintCapability<CoinType>)
Implementation
public fun initialize<CoinType>(
account: &signer,
name: string::String,
symbol: string::String,
decimals: u8,
monitor_supply: bool
): (BurnCapability<CoinType>, FreezeCapability<CoinType>, MintCapability<CoinType>) acquires CoinInfo, CoinConversionMap {
initialize_internal(
account,
name,
symbol,
decimals,
monitor_supply,
false
)
}
Function initialize_with_parallelizable_supply
Same as initialize but supply can be initialized to parallelizable aggregator.
public(friend) fun initialize_with_parallelizable_supply<CoinType>(account: &signer, name: string::String, symbol: string::String, decimals: u8, monitor_supply: bool): (coin::BurnCapability<CoinType>, coin::FreezeCapability<CoinType>, coin::MintCapability<CoinType>)
Implementation
public(friend) fun initialize_with_parallelizable_supply<CoinType>(
account: &signer,
name: string::String,
symbol: string::String,
decimals: u8,
monitor_supply: bool
): (BurnCapability<CoinType>, FreezeCapability<CoinType>, MintCapability<CoinType>) acquires CoinInfo, CoinConversionMap {
system_addresses::assert_aptos_framework(account);
initialize_internal(
account,
name,
symbol,
decimals,
monitor_supply,
true
)
}
Function initialize_internal
fun initialize_internal<CoinType>(account: &signer, name: string::String, symbol: string::String, decimals: u8, monitor_supply: bool, parallelizable: bool): (coin::BurnCapability<CoinType>, coin::FreezeCapability<CoinType>, coin::MintCapability<CoinType>)
Implementation
fun initialize_internal<CoinType>(
account: &signer,
name: string::String,
symbol: string::String,
decimals: u8,
monitor_supply: bool,
parallelizable: bool
): (BurnCapability<CoinType>, FreezeCapability<CoinType>, MintCapability<CoinType>) acquires CoinInfo, CoinConversionMap {
let account_addr = signer::address_of(account);
assert_signer_has_permission<CoinType>(account);
assert!(
coin_address<CoinType>() == account_addr,
error::invalid_argument(ECOIN_INFO_ADDRESS_MISMATCH)
);
assert!(
!exists<CoinInfo<CoinType>>(account_addr),
error::already_exists(ECOIN_INFO_ALREADY_PUBLISHED)
);
assert!(
name.length() <= MAX_COIN_NAME_LENGTH,
error::invalid_argument(ECOIN_NAME_TOO_LONG)
);
assert!(
symbol.length() <= MAX_COIN_SYMBOL_LENGTH,
error::invalid_argument(ECOIN_SYMBOL_TOO_LONG)
);
assert!(
decimals <= MAX_DECIMALS, error::invalid_argument(ECOIN_DECIMALS_TOO_LARGE)
);
let coin_info = CoinInfo<CoinType> {
name,
symbol,
decimals,
supply: if (monitor_supply) {
option::some(optional_aggregator::new(parallelizable))
} else {
option::none()
}
};
move_to(account, coin_info);
(
BurnCapability<CoinType> {},
FreezeCapability<CoinType> {},
MintCapability<CoinType> {}
)
}
Function merge
“Merges” the two given coins. The coin passed in as dst_coin will have a value equal
to the sum of the two tokens (dst_coin and source_coin).
public fun merge<CoinType>(dst_coin: &mut coin::Coin<CoinType>, source_coin: coin::Coin<CoinType>)
Implementation
public fun merge<CoinType>(
dst_coin: &mut Coin<CoinType>, source_coin: Coin<CoinType>
) {
spec {
assume dst_coin.value + source_coin.value <= MAX_U64;
};
spec {
update supply<CoinType> = supply<CoinType> - source_coin.value;
};
let Coin { value } = source_coin;
spec {
update supply<CoinType> = supply<CoinType> + value;
};
dst_coin.value += value;
}
Function mint
Mint new Coin with capability.
The capability _cap should be passed as reference to MintCapability<CoinType>.
Returns minted Coin.
public fun mint<CoinType>(amount: u64, _cap: &coin::MintCapability<CoinType>): coin::Coin<CoinType>
Implementation
public fun mint<CoinType>(
amount: u64, _cap: &MintCapability<CoinType>
): Coin<CoinType> acquires CoinInfo {
mint_internal<CoinType>(amount)
}
Function register
public fun register<CoinType>(account: &signer)
Implementation
public fun register<CoinType>(account: &signer) acquires CoinInfo, CoinConversionMap {
let account_addr = signer::address_of(account);
assert_signer_has_permission<CoinType>(account);
// Short-circuit and do nothing if account is already registered for CoinType.
if (is_account_registered<CoinType>(account_addr)) { return };
account::register_coin<CoinType>(account_addr);
}
Function transfer
Transfers amount of coins CoinType from from to to.
public entry fun transfer<CoinType>(from: &signer, to: address, amount: u64)
Implementation
public entry fun transfer<CoinType>(
from: &signer, to: address, amount: u64
) acquires CoinConversionMap, CoinInfo {
let fa =
primary_fungible_store::withdraw(
from, ensure_paired_metadata<CoinType>(), amount
);
primary_fungible_store::deposit(to, fa);
}
Function value
Returns the value passed in coin.
public fun value<CoinType>(coin: &coin::Coin<CoinType>): u64
Function withdraw
Withdraw specified amount of coin CoinType from the signing account.
public fun withdraw<CoinType>(account: &signer, amount: u64): coin::Coin<CoinType>
Implementation
public fun withdraw<CoinType>(
account: &signer, amount: u64
): Coin<CoinType> acquires CoinConversionMap, CoinInfo, PairedCoinType {
let fa =
primary_fungible_store::withdraw(
account, ensure_paired_metadata<CoinType>(), amount
);
fungible_asset_to_coin(fa)
}
Function zero
Create a new Coin<CoinType> with a value of 0.
public fun zero<CoinType>(): coin::Coin<CoinType>
Implementation
public fun zero<CoinType>(): Coin<CoinType> {
spec {
update supply<CoinType> = supply<CoinType> + 0;
};
Coin<CoinType> { value: 0 }
}
Function destroy_freeze_cap
Destroy a freeze capability. Freeze capability is dangerous and therefore should be destroyed if not used.
public fun destroy_freeze_cap<CoinType>(freeze_cap: coin::FreezeCapability<CoinType>)
Implementation
public fun destroy_freeze_cap<CoinType>(
freeze_cap: FreezeCapability<CoinType>
) {
let FreezeCapability<CoinType> {} = freeze_cap;
}
Function destroy_mint_cap
Destroy a mint capability.
public fun destroy_mint_cap<CoinType>(mint_cap: coin::MintCapability<CoinType>)
Implementation
public fun destroy_mint_cap<CoinType>(
mint_cap: MintCapability<CoinType>
) {
let MintCapability<CoinType> {} = mint_cap;
}
Function destroy_burn_cap
Destroy a burn capability.
public fun destroy_burn_cap<CoinType>(burn_cap: coin::BurnCapability<CoinType>)
Implementation
public fun destroy_burn_cap<CoinType>(
burn_cap: BurnCapability<CoinType>
) {
let BurnCapability<CoinType> {} = burn_cap;
}
Function mint_internal
fun mint_internal<CoinType>(amount: u64): coin::Coin<CoinType>
Implementation
fun mint_internal<CoinType>(amount: u64): Coin<CoinType> acquires CoinInfo {
if (amount == 0) {
return Coin<CoinType> { value: 0 }
};
let maybe_supply =
&mut borrow_global_mut<CoinInfo<CoinType>>(coin_address<CoinType>()).supply;
if (maybe_supply.is_some()) {
let supply = maybe_supply.borrow_mut();
spec {
use aptos_framework::optional_aggregator;
use aptos_framework::aggregator;
assume optional_aggregator::is_parallelizable(supply) ==>
(
aggregator::spec_aggregator_get_val(
option::borrow(supply.aggregator)
) + amount
<= aggregator::spec_get_limit(
option::borrow(supply.aggregator)
)
);
assume !optional_aggregator::is_parallelizable(supply) ==>
(
option::borrow(supply.integer).value + amount
<= option::borrow(supply.integer).limit
);
};
optional_aggregator::add(supply, (amount as u128));
};
spec {
update supply<CoinType> = supply<CoinType> + amount;
};
Coin<CoinType> { value: amount }
}
Function burn_internal
fun burn_internal<CoinType>(coin: coin::Coin<CoinType>): u64
Implementation
fun burn_internal<CoinType>(coin: Coin<CoinType>): u64 acquires CoinInfo {
spec {
update supply<CoinType> = supply<CoinType> - coin.value;
};
let Coin { value: amount } = coin;
if (amount != 0) {
let maybe_supply =
&mut borrow_global_mut<CoinInfo<CoinType>>(coin_address<CoinType>()).supply;
if (maybe_supply.is_some()) {
let supply = maybe_supply.borrow_mut();
optional_aggregator::sub(supply, (amount as u128));
};
};
amount
}
Specification
High-level Requirements
| No. | Requirement | Criticality | Implementation | Enforcement |
|---|---|---|---|---|
| 1 | Only the owner of a coin may mint, burn or freeze coins. | Critical | Acquiring capabilities for a particular CoinType may only occur if the caller has a signer for the module declaring that type. The initialize function returns these capabilities to the caller. | Formally Verified via upgrade_supply and initialize. |
| 2 | Each coin may only be created exactly once. | Medium | The initialization function may only be called once. | Formally Verified via initialize. |
| 3 | The merging of coins may only be done on coins of the same type. | Critical | The merge function is limited to merging coins of the same type only. | Formally Verified via merge. |
| 4 | The supply of a coin is only affected by burn and mint operations. | High | Only mint and burn operations on a coin alter the total supply of coins. | Formally Verified via TotalSupplyNoChange. |
| 5 | Users may register an account for a coin multiple times idempotently. | Medium | The register function should work idempotently. Importantly, it should not abort if the coin is already registered. | Formally verified via aborts_if on register. |
| 6 | Coin operations should fail if the user has not registered for the coin. | Medium | Coin operations may succeed only on valid user coin registration. | Formally Verified via balance, burn_from, freeze, unfreeze, transfer and withdraw. |
| 7 | It should always be possible to (1) determine if a coin exists, and (2) determine if a user registered an account with a particular coin. If a coin exists, it should always be possible to request the following information of the coin: (1) Name, (2) Symbol, and (3) Supply. | Low | The following functions should never abort: (1) is_coin_initialized, and (2) is_account_registered. The following functions should not abort if the coin exists: (1) name, (2) symbol, and (3) supply. | Formally Verified in corresponding functions: is_coin_initialized, is_account_registered, name, symbol and supply. |
| 8 | Coin operations should fail if the user's CoinStore is frozen. | Medium | If the CoinStore of an address is frozen, coin operations are disallowed. | Formally Verified via withdraw, transfer and deposit. |
| 9 | Utilizing AggregatableCoins does not violate other critical invariants, such as (4). | High | Utilizing AggregatableCoin does not change the real-supply of any token. | Formally Verified via TotalSupplyNoChange. |
Module-level Specification
pragma verify = true;
pragma aborts_if_is_partial;
global supply<CoinType>: num;
global aggregate_supply<CoinType>: num;
apply TotalSupplyTracked<CoinType> to *<CoinType> except initialize, initialize_internal, initialize_with_parallelizable_supply;
fun spec_fun_supply_tracked<CoinType>(val: u64, supply: Option<OptionalAggregator>): bool {
option::is_some(supply) ==>
val
== optional_aggregator::optional_aggregator_value(
option::borrow(supply)
)
}
schema TotalSupplyTracked<CoinType> {
ensures old(
spec_fun_supply_tracked<CoinType>(
supply<CoinType> + aggregate_supply<CoinType>,
global<CoinInfo<CoinType>>(type_info::type_of<CoinType>().account_address)
.supply
)
) ==>
spec_fun_supply_tracked<CoinType>(
supply<CoinType> + aggregate_supply<CoinType>,
global<CoinInfo<CoinType>>(type_info::type_of<CoinType>().account_address)
.supply
);
}
fun spec_fun_supply_no_change<CoinType>(
old_supply: Option<OptionalAggregator>, supply: Option<OptionalAggregator>
): bool {
option::is_some(old_supply) ==>
optional_aggregator::optional_aggregator_value(
option::borrow(old_supply)
) == optional_aggregator::optional_aggregator_value(
option::borrow(supply)
)
}
schema TotalSupplyNoChange<CoinType> {
let old_supply = global<CoinInfo<CoinType>>(
type_info::type_of<CoinType>().account_address
).supply;
let post supply = global<CoinInfo<CoinType>>(
type_info::type_of<CoinType>().account_address
).supply;
ensures spec_fun_supply_no_change<CoinType>(old_supply, supply);
}
Struct AggregatableCoin
#[deprecated]
struct AggregatableCoin<CoinType> has store
-
value: aggregator::Aggregator - Amount of aggregatable coin this address has.
invariant aggregator::spec_get_limit(value) == MAX_U64;
Function coin_to_fungible_asset
public fun coin_to_fungible_asset<CoinType>(coin: coin::Coin<CoinType>): fungible_asset::FungibleAsset
pragma verify = false;
let addr = type_info::type_of<CoinType>().account_address;
modifies global<CoinInfo<CoinType>>(addr);
Function fungible_asset_to_coin
fun fungible_asset_to_coin<CoinType>(fungible_asset: fungible_asset::FungibleAsset): coin::Coin<CoinType>
pragma verify = false;
Function allow_supply_upgrades
public fun allow_supply_upgrades(_aptos_framework: &signer, _allowed: bool)
Can only be updated by @aptos_framework.
aborts_if true;
Function maybe_convert_to_fungible_store
fun maybe_convert_to_fungible_store<CoinType>(account: address)
pragma verify = false;
modifies global<CoinInfo<CoinType>>(account);
modifies global<CoinStore<CoinType>>(account);
schema DepositAbortsIf<CoinType> {
account_addr: address;
let coin_store = global<CoinStore<CoinType>>(account_addr);
aborts_if !exists<CoinStore<CoinType>>(account_addr);
aborts_if coin_store.frozen;
}
Function coin_address
fun coin_address<CoinType>(): address
Get address by reflection.
pragma opaque;
aborts_if [abstract] false;
ensures [abstract] result == type_info::type_of<CoinType>().account_address;
Function balance
#[view]
public fun balance<CoinType>(owner: address): u64
pragma verify = false;
aborts_if !exists<CoinStore<CoinType>>(owner);
ensures result == global<CoinStore<CoinType>>(owner).coin.value;
Function is_coin_initialized
#[view]
public fun is_coin_initialized<CoinType>(): bool
// This enforces high-level requirement 7:
aborts_if false;
Function is_account_registered
#[view]
public fun is_account_registered<CoinType>(_account_addr: address): bool
pragma aborts_if_is_partial;
aborts_if false;
fun get_coin_supply_opt<CoinType>(): Option<OptionalAggregator> {
global<CoinInfo<CoinType>>(type_info::type_of<CoinType>().account_address).supply
}
fun spec_paired_metadata<CoinType>(): Option<Object<Metadata>> {
if (exists<CoinConversionMap>(@aptos_framework)) {
let map =
global<CoinConversionMap>(@aptos_framework).coin_to_fungible_asset_map;
if (table::spec_contains(map, type_info::type_of<CoinType>())) {
let metadata = table::spec_get(map, type_info::type_of<CoinType>());
option::spec_some(metadata)
} else {
option::spec_none()
}
} else {
option::spec_none()
}
}
fun spec_is_account_registered<CoinType>(account_addr: address): bool;
pragma aborts_if_is_partial;
aborts_if false;
ensures [abstract] result
== spec_is_account_registered<CoinType>(_account_addr);
schema CoinSubAbortsIf<CoinType> {
amount: u64;
let addr = type_info::type_of<CoinType>().account_address;
let maybe_supply = global<CoinInfo<CoinType>>(addr).supply;
include (option::is_some(
maybe_supply
)) ==> optional_aggregator::SubAbortsIf { optional_aggregator: option::borrow(maybe_supply), value: amount };
}
schema CoinAddAbortsIf<CoinType> {
amount: u64;
let addr = type_info::type_of<CoinType>().account_address;
let maybe_supply = global<CoinInfo<CoinType>>(addr).supply;
include (option::is_some(
maybe_supply
)) ==> optional_aggregator::AddAbortsIf { optional_aggregator: option::borrow(maybe_supply), value: amount };
}
schema AbortsIfNotExistCoinInfo<CoinType> {
let addr = type_info::type_of<CoinType>().account_address;
aborts_if !exists<CoinInfo<CoinType>>(addr);
}
Function name
#[view]
public fun name<CoinType>(): string::String
// This enforces high-level requirement 7:
include AbortsIfNotExistCoinInfo<CoinType>;
Function symbol
#[view]
public fun symbol<CoinType>(): string::String
// This enforces high-level requirement 7:
include AbortsIfNotExistCoinInfo<CoinType>;
Function decimals
#[view]
public fun decimals<CoinType>(): u8
include AbortsIfNotExistCoinInfo<CoinType>;
Function supply
#[view]
public fun supply<CoinType>(): option::Option<u128>
pragma verify = false;
Function coin_supply
#[view]
public fun coin_supply<CoinType>(): option::Option<u128>
let coin_addr = type_info::type_of<CoinType>().account_address;
// This enforces high-level requirement 7:
aborts_if !exists<CoinInfo<CoinType>>(coin_addr);
let maybe_supply = global<CoinInfo<CoinType>>(coin_addr).supply;
let supply = option::borrow(maybe_supply);
let value = optional_aggregator::optional_aggregator_value(supply);
ensures if (option::is_some(maybe_supply)) {
result == option::spec_some(value)
} else {
option::is_none(result)
};
Function burn
public fun burn<CoinType>(coin: coin::Coin<CoinType>, _cap: &coin::BurnCapability<CoinType>)
pragma verify = false;
let addr = type_info::type_of<CoinType>().account_address;
modifies global<CoinInfo<CoinType>>(addr);
include AbortsIfNotExistCoinInfo<CoinType>;
aborts_if coin.value == 0;
include CoinSubAbortsIf<CoinType> { amount: coin.value };
ensures supply<CoinType> == old(supply<CoinType>) - coin.value;
Function burn_from
public fun burn_from<CoinType>(account_addr: address, amount: u64, burn_cap: &coin::BurnCapability<CoinType>)
pragma verify = false;
let addr = type_info::type_of<CoinType>().account_address;
let coin_store = global<CoinStore<CoinType>>(account_addr);
let post post_coin_store = global<CoinStore<CoinType>>(account_addr);
modifies global<CoinInfo<CoinType>>(addr);
modifies global<CoinStore<CoinType>>(account_addr);
// This enforces high-level requirement 6:
aborts_if amount != 0 && !exists<CoinInfo<CoinType>>(addr);
aborts_if amount != 0 && !exists<CoinStore<CoinType>>(account_addr);
aborts_if coin_store.coin.value < amount;
let maybe_supply = global<CoinInfo<CoinType>>(addr).supply;
let supply_aggr = option::borrow(maybe_supply);
let value = optional_aggregator::optional_aggregator_value(supply_aggr);
let post post_maybe_supply = global<CoinInfo<CoinType>>(addr).supply;
let post post_supply = option::borrow(post_maybe_supply);
let post post_value = optional_aggregator::optional_aggregator_value(post_supply);
aborts_if option::is_some(maybe_supply) && value < amount;
ensures post_coin_store.coin.value == coin_store.coin.value - amount;
// This enforces high-level requirement 5 of the managed_coin module:
ensures if (option::is_some(maybe_supply)) {
post_value == value - amount
} else {
option::is_none(post_maybe_supply)
};
ensures supply<CoinType> == old(supply<CoinType>) - amount;
Function deposit
public fun deposit<CoinType>(account_addr: address, coin: coin::Coin<CoinType>)
account_addr is not frozen.
pragma verify = false;
modifies global<CoinInfo<CoinType>>(account_addr);
// This enforces high-level requirement 8:
include DepositAbortsIf<CoinType>;
ensures global<CoinStore<CoinType>>(account_addr).coin.value
== old(global<CoinStore<CoinType>>(account_addr)).coin.value + coin.value;
Function deposit_for_gas_fee
public(friend) fun deposit_for_gas_fee<CoinType>(account_addr: address, coin: coin::Coin<CoinType>)
pragma verify = false;
modifies global<CoinStore<CoinType>>(account_addr);
aborts_if !exists<CoinStore<CoinType>>(account_addr);
ensures global<CoinStore<CoinType>>(account_addr).coin.value
== old(global<CoinStore<CoinType>>(account_addr)).coin.value + coin.value;
Function destroy_zero
public fun destroy_zero<CoinType>(zero_coin: coin::Coin<CoinType>)
The value of zero_coin must be 0.
aborts_if zero_coin.value > 0;
Function extract
public fun extract<CoinType>(coin: &mut coin::Coin<CoinType>, amount: u64): coin::Coin<CoinType>
aborts_if coin.value < amount;
ensures result.value == amount;
ensures coin.value == old(coin.value) - amount;
Function extract_all
public fun extract_all<CoinType>(coin: &mut coin::Coin<CoinType>): coin::Coin<CoinType>
ensures result.value == old(coin).value;
ensures coin.value == 0;
Function freeze_coin_store
#[legacy_entry_fun]
public entry fun freeze_coin_store<CoinType>(account_addr: address, _freeze_cap: &coin::FreezeCapability<CoinType>)
pragma verify = false;
modifies global<CoinStore<CoinType>>(account_addr);
// This enforces high-level requirement 6:
aborts_if !exists<CoinStore<CoinType>>(account_addr);
let post coin_store = global<CoinStore<CoinType>>(account_addr);
ensures coin_store.frozen;
Function unfreeze_coin_store
#[legacy_entry_fun]
public entry fun unfreeze_coin_store<CoinType>(account_addr: address, _freeze_cap: &coin::FreezeCapability<CoinType>)
pragma verify = false;
modifies global<CoinStore<CoinType>>(account_addr);
// This enforces high-level requirement 6:
aborts_if !exists<CoinStore<CoinType>>(account_addr);
let post coin_store = global<CoinStore<CoinType>>(account_addr);
ensures !coin_store.frozen;
Function upgrade_supply
public entry fun upgrade_supply<CoinType>(_account: &signer)
The creator of CoinType must be @aptos_framework.
SupplyConfig allow upgrade.
aborts_if true;
Function initialize
public fun initialize<CoinType>(account: &signer, name: string::String, symbol: string::String, decimals: u8, monitor_supply: bool): (coin::BurnCapability<CoinType>, coin::FreezeCapability<CoinType>, coin::MintCapability<CoinType>)
let account_addr = signer::address_of(account);
// This enforces high-level requirement 1:
aborts_if type_info::type_of<CoinType>().account_address != account_addr;
// This enforces high-level requirement 2:
aborts_if exists<CoinInfo<CoinType>>(account_addr);
aborts_if string::length(name) > MAX_COIN_NAME_LENGTH;
aborts_if string::length(symbol) > MAX_COIN_SYMBOL_LENGTH;
Function initialize_with_parallelizable_supply
public(friend) fun initialize_with_parallelizable_supply<CoinType>(account: &signer, name: string::String, symbol: string::String, decimals: u8, monitor_supply: bool): (coin::BurnCapability<CoinType>, coin::FreezeCapability<CoinType>, coin::MintCapability<CoinType>)
let addr = signer::address_of(account);
aborts_if addr != @aptos_framework;
aborts_if monitor_supply
&& !exists<aggregator_factory::AggregatorFactory>(@aptos_framework);
include InitializeInternalSchema<CoinType> {
name: name.bytes,
symbol: symbol.bytes
};
ensures exists<CoinInfo<CoinType>>(addr);
Make sure name and symbol are legal length.
Only the creator of CoinType can initialize.
schema InitializeInternalSchema<CoinType> {
account: signer;
name: vector<u8>;
symbol: vector<u8>;
let account_addr = signer::address_of(account);
let coin_address = type_info::type_of<CoinType>().account_address;
aborts_if coin_address != account_addr;
aborts_if exists<CoinInfo<CoinType>>(account_addr);
aborts_if len(name) > MAX_COIN_NAME_LENGTH;
aborts_if len(symbol) > MAX_COIN_SYMBOL_LENGTH;
}
Function initialize_internal
fun initialize_internal<CoinType>(account: &signer, name: string::String, symbol: string::String, decimals: u8, monitor_supply: bool, parallelizable: bool): (coin::BurnCapability<CoinType>, coin::FreezeCapability<CoinType>, coin::MintCapability<CoinType>)
include InitializeInternalSchema<CoinType> {
name: name.bytes,
symbol: symbol.bytes
};
let account_addr = signer::address_of(account);
let post coin_info = global<CoinInfo<CoinType>>(account_addr);
let post supply = option::borrow(coin_info.supply);
let post value = optional_aggregator::optional_aggregator_value(supply);
let post limit = optional_aggregator::optional_aggregator_limit(supply);
modifies global<CoinInfo<CoinType>>(account_addr);
aborts_if monitor_supply
&& parallelizable
&& !exists<aggregator_factory::AggregatorFactory>(@aptos_framework);
// This enforces high-level requirement 2 of the managed_coin module:
ensures exists<CoinInfo<CoinType>>(account_addr)
&& coin_info.name == name
&& coin_info.symbol == symbol && coin_info.decimals == decimals;
ensures if (monitor_supply) {
value == 0
&& limit == MAX_U128
&& (parallelizable == optional_aggregator::is_parallelizable(supply))
} else {
option::is_none(coin_info.supply)
};
ensures result_1 == BurnCapability<CoinType> {};
ensures result_2 == FreezeCapability<CoinType> {};
ensures result_3 == MintCapability<CoinType> {};
Function merge
public fun merge<CoinType>(dst_coin: &mut coin::Coin<CoinType>, source_coin: coin::Coin<CoinType>)
// This enforces high-level requirement 3:
ensures dst_coin.value == old(dst_coin.value) + source_coin.value;
Function mint
public fun mint<CoinType>(amount: u64, _cap: &coin::MintCapability<CoinType>): coin::Coin<CoinType>
let addr = type_info::type_of<CoinType>().account_address;
modifies global<CoinInfo<CoinType>>(addr);
Function register
public fun register<CoinType>(account: &signer)
An account can only be registered once.
Updating Account.guid_creation_num will not overflow.
pragma verify = false;
Function transfer
public entry fun transfer<CoinType>(from: &signer, to: address, amount: u64)
from and to account not frozen.
from and to not the same address.
from account sufficient balance.
pragma verify = false;
let account_addr_from = signer::address_of(from);
let coin_store_from = global<CoinStore<CoinType>>(account_addr_from);
let post coin_store_post_from = global<CoinStore<CoinType>>(account_addr_from);
let coin_store_to = global<CoinStore<CoinType>>(to);
let post coin_store_post_to = global<CoinStore<CoinType>>(to);
// This enforces high-level requirement 6:
aborts_if !exists<CoinStore<CoinType>>(account_addr_from);
aborts_if !exists<CoinStore<CoinType>>(to);
// This enforces high-level requirement 8:
aborts_if coin_store_from.frozen;
aborts_if coin_store_to.frozen;
aborts_if coin_store_from.coin.value < amount;
ensures account_addr_from != to ==>
coin_store_post_from.coin.value == coin_store_from.coin.value - amount;
ensures account_addr_from != to ==>
coin_store_post_to.coin.value == coin_store_to.coin.value + amount;
ensures account_addr_from == to ==>
coin_store_post_from.coin.value == coin_store_from.coin.value;
Function withdraw
public fun withdraw<CoinType>(account: &signer, amount: u64): coin::Coin<CoinType>
Account is not frozen and sufficient balance.
pragma verify = false;
include WithdrawAbortsIf<CoinType>;
modifies global<CoinStore<CoinType>>(account_addr);
let account_addr = signer::address_of(account);
let coin_store = global<CoinStore<CoinType>>(account_addr);
let balance = coin_store.coin.value;
let post coin_post = global<CoinStore<CoinType>>(account_addr).coin.value;
ensures coin_post == balance - amount;
ensures result == Coin<CoinType> { value: amount };
schema WithdrawAbortsIf<CoinType> {
account: &signer;
amount: u64;
let account_addr = signer::address_of(account);
let coin_store = global<CoinStore<CoinType>>(account_addr);
let balance = coin_store.coin.value;
// This enforces high-level requirement 6:
aborts_if !exists<CoinStore<CoinType>>(account_addr);
// This enforces high-level requirement 8:
aborts_if coin_store.frozen;
aborts_if balance < amount;
}
Function mint_internal
fun mint_internal<CoinType>(amount: u64): coin::Coin<CoinType>
let addr = type_info::type_of<CoinType>().account_address;
modifies global<CoinInfo<CoinType>>(addr);
aborts_if (amount != 0) && !exists<CoinInfo<CoinType>>(addr);
ensures supply<CoinType> == old(supply<CoinType>) + amount;
ensures result.value == amount;
Function burn_internal
fun burn_internal<CoinType>(coin: coin::Coin<CoinType>): u64
pragma verify = false;
let addr = type_info::type_of<CoinType>().account_address;
modifies global<CoinInfo<CoinType>>(addr);