Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Module 0x1::dispatchable_fungible_asset

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::function_info;
use 0x1::fungible_asset;
use 0x1::object;
use 0x1::option;

Resource TransferRefStore

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

Constants

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;

Function 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 = &constructor_ref.generate_signer();
    move_to<TransferRefStore>(
        store_obj,
        TransferRefStore {
            transfer_ref: fungible_asset::generate_transfer_ref(constructor_ref),
        }
    );
}

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

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 (func_opt.is_some()) {
        let func = func_opt.borrow();
        function_info::load_module_from_function(func);
        let fa = dispatchable_withdraw(
            store,
            amount,
            borrow_transfer_ref(store),
            func,
        );
        fa
    } else {
        fungible_asset::unchecked_withdraw(store.object_address(), amount)
    }
}

Function 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 (func_opt.is_some()) {
        let func = func_opt.borrow();
        function_info::load_module_from_function(func);
        dispatchable_deposit(
            store,
            fa,
            borrow_transfer_ref(store),
            func
        )
    } else {
        fungible_asset::unchecked_deposit(store.object_address(), fa)
    }
}

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

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

Function 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 (func_opt.is_some()) {
        let func = func_opt.borrow();
        function_info::load_module_from_function(func);
        dispatchable_derived_balance(store, func)
    } else {
        fungible_asset::balance(store)
    }
}

Function 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 (func_opt.is_some()) {
        let func = func_opt.borrow();
        function_info::load_module_from_function(func);
        dispatchable_derived_balance(store, func) >= amount
    } else {
        fungible_asset::is_balance_at_least(store, amount)
    }
}

Function 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 (func_opt.is_some()) {
        let func = func_opt.borrow();
        function_info::load_module_from_function(func);
        dispatchable_derived_supply(metadata, func)
    } else {
        fungible_asset::supply(metadata)
    }
}

Function 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 {
    let metadata_addr = fungible_asset::store_metadata(metadata).object_address();
    assert!(
        exists<TransferRefStore>(metadata_addr),
        error::not_found(ESTORE_NOT_FOUND)
    );
    &borrow_global<TransferRefStore>(metadata_addr).transfer_ref
}

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

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

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

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

Function withdraw

public fun withdraw<T: key>(owner: &signer, store: object::Object<T>, amount: u64): fungible_asset::FungibleAsset
modifies global<permissioned_signer::PermissionStorage>(permissioned_signer::spec_permission_address(owner));
modifies global<fungible_asset::FungibleStore>(object::object_address(store));
modifies global<fungible_asset::ConcurrentFungibleBalance>(object::object_address(store));

Function deposit

public fun deposit<T: key>(store: object::Object<T>, fa: fungible_asset::FungibleAsset)
modifies global<fungible_asset::FungibleStore>(object::object_address(store));
modifies global<fungible_asset::ConcurrentFungibleBalance>(object::object_address(store));

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

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

Function dispatchable_derived_balance

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

Function dispatchable_derived_supply

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