Skip to content

dispatchable_fungible_asset - [mainnet]

This defines the fungible asset module that can issue fungible asset of any Metadata object. The metadata object can be any object that equipped with Metadata resource.

The dispatchable_fungible_asset wraps the existing fungible_asset module and adds the ability for token issuer to customize the logic for withdraw and deposit operations. For example:

  • Deflation token: a fixed percentage of token will be destructed upon transfer.
  • Transfer allowlist: token can only be transfered to addresses in the allow list.
  • Predicated transfer: transfer can only happen when some certain predicate has been met.
  • Loyalty token: a fixed loyalty will be paid to a designated address when a fungible asset transfer happens

The api listed here intended to be an in-place replacement for defi applications that uses fungible_asset api directly and is safe for non-dispatchable (aka vanilla) fungible assets as well.

See AIP-73 for further discussion

use 0x1::error;
use 0x1::features;
use 0x1::function_info;
use 0x1::fungible_asset;
use 0x1::object;
use 0x1::option;

Constants

Feature is not activated yet on the network.

const ENOT_ACTIVATED: u64 = 3;

Recipient is not getting the guaranteed value;

const EAMOUNT_MISMATCH: u64 = 2;

Dispatch target is not loaded.

const ENOT_LOADED: u64 = 4;

TransferRefStore doesn’t exist on the fungible asset type.

const ESTORE_NOT_FOUND: u64 = 1;

Resources

TransferRefStore

#[resource_group_member(#[group = 0x1::object::ObjectGroup])]
struct TransferRefStore has key
Fields
transfer_ref: fungible_asset::TransferRef

Functions

register_dispatch_functions

public fun register_dispatch_functions(constructor_ref: &object::ConstructorRef, withdraw_function: option::Option<function_info::FunctionInfo>, deposit_function: option::Option<function_info::FunctionInfo>, derived_balance_function: option::Option<function_info::FunctionInfo>)
Implementation
public fun register_dispatch_functions(
constructor_ref: &ConstructorRef,
withdraw_function: Option<FunctionInfo>,
deposit_function: Option<FunctionInfo>,
derived_balance_function: Option<FunctionInfo>,
) {
fungible_asset::register_dispatch_functions(
constructor_ref,
withdraw_function,
deposit_function,
derived_balance_function,
);
let store_obj = &object::generate_signer(constructor_ref);
move_to<TransferRefStore>(
store_obj,
TransferRefStore {
transfer_ref: fungible_asset::generate_transfer_ref(constructor_ref),
}
);
}

register_derive_supply_dispatch_function

public fun register_derive_supply_dispatch_function(constructor_ref: &object::ConstructorRef, dispatch_function: option::Option<function_info::FunctionInfo>)
Implementation
public fun register_derive_supply_dispatch_function(
constructor_ref: &ConstructorRef,
dispatch_function: Option<FunctionInfo>
) {
fungible_asset::register_derive_supply_dispatch_function(
constructor_ref,
dispatch_function
);
}

withdraw

Withdraw amount of the fungible asset from store by the owner.

The semantics of deposit will be governed by the function specified in DispatchFunctionStore.

public fun withdraw<T: key>(owner: &signer, store: object::Object<T>, amount: u64): fungible_asset::FungibleAsset
Implementation
public fun withdraw<T: key>(
owner: &signer,
store: Object<T>,
amount: u64,
): FungibleAsset acquires TransferRefStore {
fungible_asset::withdraw_sanity_check(owner, store, false);
fungible_asset::withdraw_permission_check(owner, store, amount);
let func_opt = fungible_asset::withdraw_dispatch_function(store);
if (option::is_some(&func_opt)) {
assert!(
features::dispatchable_fungible_asset_enabled(),
error::aborted(ENOT_ACTIVATED)
);
let func = option::borrow(&func_opt);
function_info::load_module_from_function(func);
let fa = dispatchable_withdraw(
store,
amount,
borrow_transfer_ref(store),
func,
);
fa
} else {
fungible_asset::unchecked_withdraw(object::object_address(&store), amount)
}
}

deposit

Deposit amount of the fungible asset to store.

The semantics of deposit will be governed by the function specified in DispatchFunctionStore.

public fun deposit<T: key>(store: object::Object<T>, fa: fungible_asset::FungibleAsset)
Implementation
public fun deposit<T: key>(store: Object<T>, fa: FungibleAsset) acquires TransferRefStore {
fungible_asset::deposit_sanity_check(store, false);
let func_opt = fungible_asset::deposit_dispatch_function(store);
if (option::is_some(&func_opt)) {
assert!(
features::dispatchable_fungible_asset_enabled(),
error::aborted(ENOT_ACTIVATED)
);
let func = option::borrow(&func_opt);
function_info::load_module_from_function(func);
dispatchable_deposit(
store,
fa,
borrow_transfer_ref(store),
func
)
} else {
fungible_asset::unchecked_deposit(object::object_address(&store), fa)
}
}

transfer

Transfer an amount of fungible asset from from_store, which should be owned by sender, to receiver. Note: it does not move the underlying object.

public entry fun transfer<T: key>(sender: &signer, from: object::Object<T>, to: object::Object<T>, amount: u64)
Implementation
public entry fun transfer<T: key>(
sender: &signer,
from: Object<T>,
to: Object<T>,
amount: u64,
) acquires TransferRefStore {
let fa = withdraw(sender, from, amount);
deposit(to, fa);
}

transfer_assert_minimum_deposit

Transfer an amount of fungible asset from from_store, which should be owned by sender, to receiver. The recipient is guranteed to receive asset greater than the expected amount. Note: it does not move the underlying object.

public entry fun transfer_assert_minimum_deposit<T: key>(sender: &signer, from: object::Object<T>, to: object::Object<T>, amount: u64, expected: u64)
Implementation
public entry fun transfer_assert_minimum_deposit<T: key>(
sender: &signer,
from: Object<T>,
to: Object<T>,
amount: u64,
expected: u64
) acquires TransferRefStore {
let start = fungible_asset::balance(to);
let fa = withdraw(sender, from, amount);
deposit(to, fa);
let end = fungible_asset::balance(to);
assert!(end - start >= expected, error::aborted(EAMOUNT_MISMATCH));
}

derived_balance

Get the derived value of store using the overloaded hook.

The semantics of value will be governed by the function specified in DispatchFunctionStore.

#[view]
public fun derived_balance<T: key>(store: object::Object<T>): u64
Implementation
public fun derived_balance<T: key>(store: Object<T>): u64 {
let func_opt = fungible_asset::derived_balance_dispatch_function(store);
if (option::is_some(&func_opt)) {
assert!(
features::dispatchable_fungible_asset_enabled(),
error::aborted(ENOT_ACTIVATED)
);
let func = option::borrow(&func_opt);
function_info::load_module_from_function(func);
dispatchable_derived_balance(store, func)
} else {
fungible_asset::balance(store)
}
}

is_derived_balance_at_least

Whether the derived value of store using the overloaded hook is at least amount

The semantics of value will be governed by the function specified in DispatchFunctionStore.

#[view]
public fun is_derived_balance_at_least<T: key>(store: object::Object<T>, amount: u64): bool
Implementation
public fun is_derived_balance_at_least<T: key>(store: Object<T>, amount: u64): bool {
let func_opt = fungible_asset::derived_balance_dispatch_function(store);
if (option::is_some(&func_opt)) {
assert!(
features::dispatchable_fungible_asset_enabled(),
error::aborted(ENOT_ACTIVATED)
);
let func = option::borrow(&func_opt);
function_info::load_module_from_function(func);
dispatchable_derived_balance(store, func) >= amount
} else {
fungible_asset::is_balance_at_least(store, amount)
}
}

derived_supply

Get the derived supply of the fungible asset using the overloaded hook.

The semantics of supply will be governed by the function specified in DeriveSupplyDispatch.

#[view]
public fun derived_supply<T: key>(metadata: object::Object<T>): option::Option<u128>
Implementation
public fun derived_supply<T: key>(metadata: Object<T>): Option<u128> {
let func_opt = fungible_asset::derived_supply_dispatch_function(metadata);
if (option::is_some(&func_opt)) {
assert!(
features::dispatchable_fungible_asset_enabled(),
error::aborted(ENOT_ACTIVATED)
);
let func = option::borrow(&func_opt);
function_info::load_module_from_function(func);
dispatchable_derived_supply(metadata, func)
} else {
fungible_asset::supply(metadata)
}
}

borrow_transfer_ref

fun borrow_transfer_ref<T: key>(metadata: object::Object<T>): &fungible_asset::TransferRef
Implementation
inline fun borrow_transfer_ref<T: key>(metadata: Object<T>): &TransferRef acquires TransferRefStore {
let metadata_addr = object::object_address(
&fungible_asset::store_metadata(metadata)
);
assert!(
exists<TransferRefStore>(metadata_addr),
error::not_found(ESTORE_NOT_FOUND)
);
&borrow_global<TransferRefStore>(metadata_addr).transfer_ref
}

dispatchable_withdraw

fun dispatchable_withdraw<T: key>(store: object::Object<T>, amount: u64, transfer_ref: &fungible_asset::TransferRef, function: &function_info::FunctionInfo): fungible_asset::FungibleAsset
Implementation
native fun dispatchable_withdraw<T: key>(
store: Object<T>,
amount: u64,
transfer_ref: &TransferRef,
function: &FunctionInfo,
): FungibleAsset;

dispatchable_deposit

fun dispatchable_deposit<T: key>(store: object::Object<T>, fa: fungible_asset::FungibleAsset, transfer_ref: &fungible_asset::TransferRef, function: &function_info::FunctionInfo)
Implementation
native fun dispatchable_deposit<T: key>(
store: Object<T>,
fa: FungibleAsset,
transfer_ref: &TransferRef,
function: &FunctionInfo,
);

dispatchable_derived_balance

fun dispatchable_derived_balance<T: key>(store: object::Object<T>, function: &function_info::FunctionInfo): u64
Implementation
native fun dispatchable_derived_balance<T: key>(
store: Object<T>,
function: &FunctionInfo,
): u64;

dispatchable_derived_supply

fun dispatchable_derived_supply<T: key>(metadata: object::Object<T>, function: &function_info::FunctionInfo): option::Option<u128>
Implementation
native fun dispatchable_derived_supply<T: key>(
metadata: Object<T>,
function: &FunctionInfo,
): Option<u128>;

Specification

pragma verify = false;

dispatchable_withdraw

fun dispatchable_withdraw<T: key>(store: object::Object<T>, amount: u64, transfer_ref: &fungible_asset::TransferRef, function: &function_info::FunctionInfo): fungible_asset::FungibleAsset
pragma opaque;

dispatchable_deposit

fun dispatchable_deposit<T: key>(store: object::Object<T>, fa: fungible_asset::FungibleAsset, transfer_ref: &fungible_asset::TransferRef, function: &function_info::FunctionInfo)
pragma opaque;

dispatchable_derived_balance

fun dispatchable_derived_balance<T: key>(store: object::Object<T>, function: &function_info::FunctionInfo): u64
pragma opaque;

dispatchable_derived_supply

fun dispatchable_derived_supply<T: key>(metadata: object::Object<T>, function: &function_info::FunctionInfo): option::Option<u128>
pragma opaque;