Skip to content

overloadable_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 overloadable_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

See AIP-73 for further discussion

use 0x1::error;
use 0x1::function_info;
use 0x1::fungible_asset;
use 0x1::object;
use 0x1::signer;
use 0x1::string;

Constants

Trying to register overload functions to fungible asset that has already been initialized with custom transfer function.

const EALREADY_REGISTERED: u64 = 5;

Fungibility is only available for non-deletable objects.

const EOBJECT_IS_DELETABLE: u64 = 18;

Recipient is not getting the guaranteed value;

const EAMOUNT_MISMATCH: u64 = 4;

Provided deposit function type doesn’t meet the signature requirement.

const EDEPOSIT_FUNCTION_SIGNATURE_MISMATCH: u64 = 2;

Calling overloadable api on non-overloadable fungible asset store.

const EFUNCTION_STORE_NOT_FOUND: u64 = 3;

Provided withdraw function type doesn’t meet the signature requirement.

const EWITHDRAW_FUNCTION_SIGNATURE_MISMATCH: u64 = 1;

Resources

OverloadFunctionStore

#[resource_group_member(#[group = 0x1::object::ObjectGroup])]
struct OverloadFunctionStore has key
Fields
withdraw_function: function_info::FunctionInfo
deposit_function: function_info::FunctionInfo
transfer_ref: fungible_asset::TransferRef

Functions

register_overload_functions

Create a fungible asset store whose transfer rule would be overloaded by the provided function.

public fun register_overload_functions(constructor_ref: &object::ConstructorRef, withdraw_function: function_info::FunctionInfo, deposit_function: function_info::FunctionInfo)
Implementation
public fun register_overload_functions(
constructor_ref: &ConstructorRef,
withdraw_function: FunctionInfo,
deposit_function: FunctionInfo,
) {
let dispatcher_withdraw_function_info = function_info::new_function_info(
@aptos_framework,
string::utf8(b"overloadable_fungible_asset"),
string::utf8(b"dispatchable_withdraw"),
);
// Verify that caller type matches callee type so wrongly typed function cannot be registered.
assert!(function_info::check_dispatch_type_compatibility(
&dispatcher_withdraw_function_info,
&withdraw_function
), error::invalid_argument(EWITHDRAW_FUNCTION_SIGNATURE_MISMATCH));
let dispatcher_deposit_function_info = function_info::new_function_info(
@aptos_framework,
string::utf8(b"overloadable_fungible_asset"),
string::utf8(b"dispatchable_deposit"),
);
// Verify that caller type matches callee type so wrongly typed function cannot be registered.
assert!(function_info::check_dispatch_type_compatibility(
&dispatcher_deposit_function_info,
&deposit_function
), error::invalid_argument(EDEPOSIT_FUNCTION_SIGNATURE_MISMATCH));
assert!(!object::can_generate_delete_ref(constructor_ref), error::invalid_argument(EOBJECT_IS_DELETABLE));
assert!(!exists<OverloadFunctionStore>(object::address_from_constructor_ref(constructor_ref)), error::already_exists(EALREADY_REGISTERED));
// Freeze the FungibleStore to force usign the new overloaded api.
let extend_ref = object::generate_extend_ref(constructor_ref);
fungible_asset::set_global_frozen_flag(&extend_ref, true);
let store_obj = &object::generate_signer(constructor_ref);
// Store the overload function hook.
move_to<OverloadFunctionStore>(store_obj, OverloadFunctionStore {
withdraw_function,
deposit_function,
transfer_ref: fungible_asset::generate_transfer_ref(constructor_ref),
});
}

withdraw

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

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

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 OverloadFunctionStore {
let metadata_addr = object::object_address(&fungible_asset::store_metadata(store));
let owner_address = signer::address_of(owner);
assert!(exists<OverloadFunctionStore>(metadata_addr), error::not_found(EFUNCTION_STORE_NOT_FOUND));
let overloadable_store = borrow_global<OverloadFunctionStore>(metadata_addr);
dispatchable_withdraw(
owner_address,
store,
amount,
&overloadable_store.transfer_ref,
&overloadable_store.withdraw_function,
)
}

deposit

Deposit amount of the fungible asset to store.

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

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 OverloadFunctionStore {
let metadata_addr = object::object_address(&fungible_asset::store_metadata(store));
assert!(exists<OverloadFunctionStore>(metadata_addr), error::not_found(EFUNCTION_STORE_NOT_FOUND));
let overloadable_store = borrow_global<OverloadFunctionStore>(metadata_addr);
dispatchable_deposit(
store,
fa,
&overloadable_store.transfer_ref,
&overloadable_store.deposit_function,
);
}

transfer_fixed_send

A transfer with a fixed amount debited from the sender

public fun transfer_fixed_send<T: key>(_sender: &signer, from: object::Object<T>, to: object::Object<T>, send_amount: u64)
Implementation
public fun transfer_fixed_send<T: key>(
_sender: &signer,
from: Object<T>,
to: Object<T>,
send_amount: u64,
) acquires OverloadFunctionStore {
let metadata_addr = object::object_address(&fungible_asset::store_metadata(from));
assert!(exists<OverloadFunctionStore>(metadata_addr), error::not_found(EFUNCTION_STORE_NOT_FOUND));
let overloadable_store = borrow_global<OverloadFunctionStore>(metadata_addr);
let fa = fungible_asset::withdraw_with_ref(&overloadable_store.transfer_ref, from, send_amount);
deposit(to, fa);
}

transfer_fixed_receive

A transfer with a fixed amount credited to the recipient

public fun transfer_fixed_receive<T: key>(sender: &signer, from: object::Object<T>, to: object::Object<T>, receive_amount: u64)
Implementation
public fun transfer_fixed_receive<T: key>(
sender: &signer,
from: Object<T>,
to: Object<T>,
receive_amount: u64,
) acquires OverloadFunctionStore {
let fa = withdraw(sender, from, receive_amount);
let metadata_addr = object::object_address(&fungible_asset::store_metadata(from));
let overloadable_store = borrow_global<OverloadFunctionStore>(metadata_addr);
assert!(fungible_asset::amount(&fa) == receive_amount, error::aborted(EAMOUNT_MISMATCH));
fungible_asset::deposit_with_ref(&overloadable_store.transfer_ref, to, fa);
}

dispatchable_withdraw

fun dispatchable_withdraw<T: key>(owner: address, 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>(
owner: address,
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,
);