AptosMove实践指南:构建并部署同质化代币水龙头(FAFaucet)在Aptos区块链上,Move编程语言为开发者提供了强大的工具来创建、部署和管理代币。在本教程中,我们将使用AptosMove创建和部署一个同质化代币(FA)水龙头合约,通过它,用户可以轻松铸造并接收代币
在 Aptos 区块链上,Move 编程语言为开发者提供了强大的工具来创建、部署和管理代币。在本教程中,我们将使用 Aptos Move 创建和部署一个同质化代币(FA)水龙头合约,通过它,用户可以轻松铸造并接收代币。本教程适合对 Aptos 和 Move 有基础了解的开发者,并将通过实际代码示例一步步展示如何完成这一过程。
本文介绍了如何使用 Aptos Move 构建并部署一个同质化代币水龙头(FA Faucet)。文章详细讲解了如何创建不可删除的对象、生成权限引用、铸造代币并将其转移到指定账户。此外,还展示了项目的目录结构、Move.toml 文件配置以及智能合约的编写与测试过程。通过本教程,读者将能够掌握 Aptos Move 在代币创建和管理方面的关键技术,实现一个功能齐全的代币水龙头应用。
从高层次来看,这是通过以下方式实现的:
Metadata
。Ref
s 以启用任何所需的权限。At a high level, this is done by:
Metadata
.Ref
s to enable any desired permissions.To create an FA, first you need to create a non-deletable Object since destroying the metadata for a Fungible Asset while there are active balances would not make sense. You can do that by either calling object::create_named_object(caller_address, NAME)
or object::create_sticky_object(caller_address)
to create the Object on-chain.
当您调用这些函数时,它们将返回一个ConstructorRef
。Ref
允许在创建对象后立即对其进行自定义。 您可以使用ConstructorRef
根据您的用例生成可能需要的其他权限。
请注意,ConstructorRef
无法存储,并且会在用于创建此对象的事务结束时销毁,因此任何Ref
s 都必须在对象创建期间生成。
ConstructorRef
的一个用途是生成 FAMetadata
对象。
标准库提供了一个方法primary_fungible_store::create_primary_store_enabled_fungible_asset
,该函数允许将您的可替代资产转移到任何帐户。
创建元数据后,您还可以使用它ConstructorRef
来生成其他内容
faucet
项目实操mkdir faucet
cd faucet
aptos init
aptos move init --name faucet
open -a RustRover .
tree . -L 6 -I 'build'
.
├── Move.toml
├── scripts
├── sources
│ └── faucet.move
└── tests
4 directories, 2 files
Move.toml
文件[package]
name = "faucet"
version = "1.0.0"
authors = []
[addresses]
contract = "4cb64ff8439e8a7eff1e513413f711b4ce14ff95b35e2717156496375a3b676e"
[dev-addresses]
[dependencies.AptosFramework]
git = "https://github.com/aptos-labs/aptos-core.git"
rev = "mainnet"
subdir = "aptos-move/framework/aptos-framework"
[dev-dependencies]
faucet.move
文件module contract::faucet {
use aptos_framework::fungible_asset::{
Self,
TransferRef,
BurnRef,
Metadata,
FungibleAsset,
MintRef
};
use aptos_framework::object::{Self, Object};
use aptos_framework::primary_fungible_store;
use std::string::utf8;
use std::option;
use std::signer;
#[test_only]
use aptos_framework::account;
const ASSET_SYMBOL: vector<u8> = b"FA";
struct MyMintRef has key {
mint_ref: MintRef
}
struct MyMintRef2 has key {
admin: address,
mint_ref: MintRef
}
struct MyTransferRef has key {
transfer_ref: TransferRef
}
struct MyBurnRef has key {
burn_ref: BurnRef
}
fun init_module(contract: &signer) {
let constructor_ref = object::create_named_object(contract, ASSET_SYMBOL);
primary_fungible_store::create_primary_store_enabled_fungible_asset(
&constructor_ref,
option::none(),
utf8(b"FA Coin"), /* name */
utf8(ASSET_SYMBOL), /* symbol */
8, /* decimals */
utf8(b"http://example.com/favicon.ico"), /* icon */
utf8(b"http://example.com") /* project */
);
let mint_ref = fungible_asset::generate_mint_ref(&constructor_ref);
let transfer_ref = fungible_asset::generate_transfer_ref(&constructor_ref);
let burn_ref = fungible_asset::generate_burn_ref(&constructor_ref);
move_to(
contract,
MyMintRef { mint_ref }
);
move_to(
contract,
MyBurnRef { burn_ref }
);
move_to(
contract,
MyTransferRef { transfer_ref }
);
}
entry fun create_fa (
sender: &signer
){
let constructor_ref = object::create_named_object(sender, ASSET_SYMBOL);
primary_fungible_store::create_primary_store_enabled_fungible_asset(
&constructor_ref,
option::none(),
utf8(b"FA2 Coin"), /* name */
utf8(b"FA2"), /* symbol */
8, /* decimals */
utf8(b"http://example.com/favicon.ico"), /* icon */
utf8(b"http://example.com"), /* project */
);
let mint_ref = fungible_asset::generate_mint_ref(&constructor_ref);
let transfer_ref = fungible_asset::generate_transfer_ref(&constructor_ref);
let burn_ref = fungible_asset::generate_burn_ref(&constructor_ref);
move_to(
sender,
MyMintRef2 {
admin: signer::address_of(sender),
mint_ref
}
);
move_to(
sender,
MyBurnRef {
burn_ref
}
);
move_to(
sender,
MyTransferRef {
transfer_ref
}
);
}
entry fun faucet(sender: &signer, amount: u64) acquires MyMintRef {
let my_mint_ref = borrow_global<MyMintRef>(@contract);
let fa = fungible_asset::mint(&my_mint_ref.mint_ref, amount);
primary_fungible_store::deposit(signer::address_of(sender), fa);
}
entry fun mint(
sender: &signer,
amount: u64
) acquires MyMintRef2 {
let my_mint_ref2 = borrow_global<MyMintRef2>(signer::address_of(sender));
assert!(
my_mint_ref2.admin == signer::address_of(sender),
123
);
let fa = fungible_asset::mint(&my_mint_ref2.mint_ref, amount);
primary_fungible_store::deposit( signer::address_of(sender), fa);
}
#[view]
/// Get the balance of `account`'s primary store.
public fun balance<T: key>(account: address, metadata: Object<T>): u64 {
primary_fungible_store::balance(account, metadata)
}
#[view]
public fun is_balance_at_least<T: key>(
account: address, metadata: Object<T>, amount: u64
): bool {
primary_fungible_store::is_balance_at_least(account, metadata, amount)
}
#[test]
fun test() acquires MyMintRef {
let user_signer = account::create_account_for_test(@contract);
init_module(&user_signer);
let user1_signer = account::create_account_for_test(@0x12345);
faucet(&user1_signer, 1 * 1000 * 1000 * 100);
}
#[test]
fun test_mint() acquires MyMintRef2 {
let user_signer = account::create_account_for_test(@contract);
create_fa(&user_signer);
mint(&user_signer, 1 * 1000 * 1000 * 100);
mint(&user_signer, 1 * 1000 * 1000 * 100);
}
}
init_module publish 的时候会自动调用
faucet
方法mint
方法/opt/homebrew/bin/aptos move publish
Compiling, may take a little while to download git dependencies...
UPDATING GIT DEPENDENCY https://github.com/aptos-labs/aptos-core.git
INCLUDING DEPENDENCY AptosFramework
INCLUDING DEPENDENCY AptosStdlib
INCLUDING DEPENDENCY MoveStdlib
BUILDING faucet
warning[W09001]: unused alias
┌─ /Users/qiaopengjun/Code/Aptos/hello_aptos/faucet/sources/faucet.move:6:9
│
6 │ Metadata,
│ ^^^^^^^^ Unused 'use' of alias 'Metadata'. Consider removing it
warning[W09001]: unused alias
┌─ /Users/qiaopengjun/Code/Aptos/hello_aptos/faucet/sources/faucet.move:7:9
│
7 │ FungibleAsset,
│ ^^^^^^^^^^^^^ Unused 'use' of alias 'FungibleAsset'. Consider removing it
warning: unused alias
┌─ /Users/qiaopengjun/Code/Aptos/hello_aptos/faucet/sources/faucet.move:6:9
│
6 │ Metadata,
│ ^^^^^^^^ Unused 'use' of alias 'Metadata'. Consider removing it
warning: unused alias
┌─ /Users/qiaopengjun/Code/Aptos/hello_aptos/faucet/sources/faucet.move:7:9
│
7 │ FungibleAsset,
│ ^^^^^^^^^^^^^ Unused 'use' of alias 'FungibleAsset'. Consider removing it
package size 2931 bytes
Do you want to submit a transaction for a range of [404100 - 606100] Octas at a gas unit price of 100 Octas? [yes/no] >
yes
Transaction submitted: https://explorer.aptoslabs.com/txn/0x78f2e8b66560426445fa5b1c490fbdc6264393d500717b19066cecd13279709a?network=testnet
{
"Result": {
"transaction_hash": "0x78f2e8b66560426445fa5b1c490fbdc6264393d500717b19066cecd13279709a",
"gas_used": 4041,
"gas_unit_price": 100,
"sender": "4cb64ff8439e8a7eff1e513413f711b4ce14ff95b35e2717156496375a3b676e",
"sequence_number": 0,
"success": true,
"timestamp_us": 1727359014128512,
"version": 6043585322,
"vm_status": "Executed successfully"
}
}
Process finished with exit code 0
faucet
方法 点击 RunApprove
faucet
方法成功执行balance
方法第一种方式:
第二种方式:
第三种方式:
is_balance_at_least
方法查看余额至少 100000000,应该返回 true
查看余额至少 200000000,应该返回 false
create_fa
方法mint
方法mint
方法验证可以看到报错了,该账户没有权限Mint,成功验证只有 create fa2 的人才可以 Mint
注意:关于 IDE 的设置,请参考我之前的文章:
Aptos 开发指南:在 JetBrains 编辑器中配置运行、编译、测试与发布部署,实现更高效开发
/// Create a new named object and return the ConstructorRef. Named objects can be queried globally
/// by knowing the user generated seed used to create them. Named objects cannot be deleted.
public fun create_named_object(creator: &signer, seed: vector<u8>): ConstructorRef {
let creator_address = signer::address_of(creator);
let obj_addr = create_object_address(&creator_address, seed);
create_object_internal(creator_address, obj_addr, false)
}
/// Create a new object by generating a random unique address based on transaction hash.
/// The unique address is computed sha3_256([transaction hash | auid counter | 0xFB]).
/// The created object is deletable as we can guarantee the same unique address can
/// never be regenerated with future txs.
public fun create_object(owner_address: address): ConstructorRef {
let unique_address = transaction_context::generate_auid_address();
create_object_internal(owner_address, unique_address, true)
}
/// Same as `create_object` except the object to be created will be undeletable.
public fun create_sticky_object(owner_address: address): ConstructorRef {
let unique_address = transaction_context::generate_auid_address();
create_object_internal(owner_address, unique_address, false)
}
public fun create_primary_store_enabled_fungible_asset(
constructor_ref: &ConstructorRef,
maximum_supply: Option<u128>,
name: String,
symbol: String,
decimals: u8,
icon_uri: String,
project_uri: String,
) {
fungible_asset::add_fungibility(
constructor_ref,
maximum_supply,
name,
symbol,
decimals,
icon_uri,
project_uri,
);
let metadata_obj = &object::generate_signer(constructor_ref);
move_to(metadata_obj, DeriveRefPod {
metadata_derive_ref: object::generate_derive_ref(constructor_ref),
});
}
public fun generate_mint_ref(constructor_ref: &ConstructorRef): MintRef {
let metadata = object::object_from_constructor_ref<Metadata>(constructor_ref);
MintRef { metadata }
}
struct MintRef has drop, store {
metadata: Object<Metadata>
}
/// Mint the specified `amount` of the fungible asset.
public fun mint(ref: &MintRef, amount: u64): FungibleAsset acquires Supply, ConcurrentSupply {
let metadata = ref.metadata;
mint_internal(metadata, amount)
}
/// Deposit fungible asset `fa` to the given account's primary store.
public fun deposit(owner: address, fa: FungibleAsset) acquires DeriveRefPod {
let metadata = fungible_asset::asset_metadata(&fa);
let store = ensure_primary_store_exists(owner, metadata);
dispatchable_fungible_asset::deposit(store, fa);
}
#[test_only]
public fun create_account_for_test(new_address: address): signer {
// Make this easier by just allowing the account to be created again in a test
if (!exists_at(new_address)) {
create_account_unchecked(new_address)
} else {
create_signer_for_test(new_address)
}
}
通过本篇文章的学习,我们成功在 Aptos 区块链上使用 Move 构建了一个同质化代币水龙头。我们从代币的创建、权限管理到代币的铸造与分发,全面覆盖了相关步骤和代码实现。Aptos Move 提供了强大的灵活性和功能,使得构建代币系统变得高效简洁。通过这些实践经验,读者可以进一步探索 Aptos 生态系统的其他应用,并将代币水龙头功能扩展到更多的场景中。
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!