Transfers

Transfers are a necessary feature for most fungible assets, and definitely for a stablecoin. We can allow an owner of FA units in a fungible store to transfer by creating the following function.


public entry fun transfer(
from: &signer,
to: address,
amount: u64,
) acquires Management, State {
assert_not_denylisted(from);
assert_not_denylisted(to);
// view function will be covered in the next section
let metadata_object = metadata();
// retrieve primary wallets
let from_wallet = primary_fungible_store::primary_store(from_addr, metadata_object);
let to_wallet = primary_fungible_store::ensure_primary_store_exists(to, metadata_object);
fungible_asset::transfer(from, from_wallet, to_wallet, amount);
}

Another option could be to enable another entity (spender) to transfer FA units on behalf of a user. This is a common theme seen in EVM implementations of stablecoins. Below is an example of how that that might be done. Note that specified "allowances" like the ones seen in ERC-20 approve and permit are not used here. Only approvals for spending an arbitrary amount.


/// Allow a spender to transfer tokens from the owner's account given their signed approval.
/// Caller needs to provide the from account's scheme and public key which can be gotten via the Aptos SDK.
public fun transfer_from(
spender: &signer,
proof: vector<u8>,
from: address,
from_account_scheme: u8,
from_public_key: vector<u8>,
to: address,
amount: u64,
) acquires Management, State {
assert_not_denylisted(from);
assert_not_denylisted(to);
let expected_message = Approval {
owner: from,
nonce: account::get_sequence_number(from),
chain_id: chain_id::get(),
spender: signer::address_of(spender),
amount,
};
account::verify_signed_message(from, from_account_scheme, from_public_key, proof, expected_message);
let transfer_ref = &borrow_global<Management>(usdk_address()).transfer_ref;
primary_fungible_store::transfer_with_ref(transfer_ref, from, to, amount);
}

There are certainly a lot of added parts above. It is out of the scope of this tutorial to explain the concepts related to signed verification, but feel free to explore the respective modules! Note that if you do choose to implement this, the Approval struct can be defined like this:


struct Approval has drop {
owner: address,
nonce: u64,
chain_id: u8,
spender: address,
amount: u64,
}