Aptos Move 可兑换资产示例套件:Managed Fungible Asset 从铸币到稳定币的完整实践指南
Aptos Move 可兑换资产示例套件Managed Fungible Asset 从铸币到稳定币的完整实践指南【免费下载链接】aptos-coreAptos is a layer 1 blockchain built to support the widespread use of blockchain through better technology and user experience.项目地址: https://gitcode.com/GitHub_Trending/ap/aptos-coreAptos 的 Move 语言中Fungible Asset可兑换资产FA框架让开发者无需直接继承原生 Coin 模块即可发行具备冻结、增发、销毁等管理能力的资产。本文基于仓库aptos-move/move-examples/fungible_asset/目录下的 6 组官方示例README完整讲解每类资产的发行方式、管理引用MintRef/TransferRef/BurnRef的授予策略、冻结与暂停机制以及如何用多签账户和角色体系来管理资产帮助读者掌握在 Aptos 上从轻量代币到合规稳定币的全链路合约写法。目录总览六种可兑换资产示例fungible_asset目录按能力递进组织了六组示例覆盖托管可兑换资产Managed Fungible Asset的各种管理场景子目录定位核心能力managed_fungible_asset通用基础模块元数据所有者可控的铸造、转账、销毁、冻结、存取coin_example.move轻量发币示例只面向主存储Primary Store的简化封装managed_fungible_token对象化代币用 Token 对象含 Collection作为元数据对象preminted_managed_coin预铸币初始化时一次性铸完全部供应量之后不可增发multisig_managed_coin多签托管用多签账户持有并管理资产fa_coin一体化模块部署即发币内置暂停开关与可派发转账钩子stablecoin/usdk.move托管稳定币角色管理master minter/minter/pauser/denylister 黑白名单 暂停所有示例都构建在aptos_framework::fungible_asset之上其核心思想是资产的铸造、转账、销毁能力以引用资源MintRef、TransferRef、BurnRef的形式从元数据对象上派生出来谁持有引用谁就拥有对应管理能力。下文逐组展开。基础模块 managed_fungible_asset以引用资源为核心的管理能力managed_fungible_asset.move 是整个目录的中枢模块。模块头部的注释明确了它提供的五类功能以元数据对象所有者身份向 Fungible Store 铸造资产以所有者身份在 Fungible Store 之间转账忽略frozen标志以所有者身份从 Fungible Store 销毁资产以所有者身份从 Fungible Store 提取已合并的资产以所有者身份向 Fungible Store 存入资产。能力开关ref_flags 与 ManagingRefs模块的关键设计是ManagingRefs资源——它把三个引用包装成Option并挂载到元数据对象上const ERR_NOT_OWNER: u64 1; const ERR_INVALID_REF_FLAGS_LENGTH: u64 2; const ERR_VECTORS_LENGTH_MISMATCH: u64 3; const ERR_MINT_REF: u64 4; const ERR_TRANSFER_REF: u64 5; const ERR_BURN_REF: u64 6; #[resource_group_member(group aptos_framework::object::ObjectGroup)] /// Hold refs to control the minting, transfer and burning of fungible assets. struct ManagingRefs has key { mint_ref: OptionMintRef, transfer_ref: OptionTransferRef, burn_ref: OptionBurnRef, }错误码定义清晰区分了非所有者0x1、参数错误引用标志长度必须为 3、向量长度不匹配和引用不存在三类失败便于链上排查。initialize函数是资产的入口参数与行为如下参数说明constructor_ref由object::create_named_object生成的构造引用决定元数据对象地址maximum_supply最大供应量0表示不限制内部转换为option::none()name/symbol/decimals/icon_uri/project_uri标准元数据字段ref_flags长度为 3 的vectorbool分别决定是否生成MintRef、TransferRef、BurnRefpublic fun initialize( constructor_ref: ConstructorRef, maximum_supply: u128, name: String, symbol: String, decimals: u8, icon_uri: String, project_uri: String, ref_flags: vectorbool, ) { assert!(vector::length(ref_flags) 3, error::invalid_argument(ERR_INVALID_REF_FLAGS_LENGTH)); ... primary_fungible_store::create_primary_store_enabled_fungible_asset(...); // 按 ref_flags 选择性生成三类引用存入 ManagingRefs }注意该模块使用create_primary_store_enabled_fungible_asset创建资产意味着每个账户都可以拥有该资产的主存储Primary Fungible Store——即资产余额可直接挂在账户上像原生 Coin 一样参与转账和收款而不必依赖一个单独的 Store 对象。批量管理 API模块为每类操作提供两对入口面向vectoraddress的简版内部自动解析/确保主存储存在和面向vectorObjectFungibleStore的通用版可操作任意 Store铸造mint_to_primary_stores/mint转账忽略冻结transfer_between_primary_stores/transfer底层调用fungible_asset::transfer_with_ref销毁burn_from_primary_stores/burn冻结/解冻set_primary_stores_frozen_status/set_frozen_status底层调用set_frozen_flag提取withdraw_from_primary_stores/withdraw多次withdraw_with_ref后用merge合并为单个FungibleAsset返回存入deposit_to_primary_stores/deposit通过extract从源资产中拆出指定金额再deposit_with_ref所有管理函数都先经过authorized_borrow_*_ref校验inline fun authorized_borrow_refs( owner: signer, asset: ObjectMetadata, ): ManagingRefs acquires ManagingRefs { assert!(object::is_owner(asset, signer::address_of(owner)), error::permission_denied(ERR_NOT_OWNER)); borrow_globalManagingRefs(object::object_address(asset)) }即调用者的地址必须等于元数据对象的所有者地址否则 abort0x1。这个权限模型与原生 Coin 的Treasury思路一致只是把权限载体从账户资源换成了对象所有者。内嵌测试验证基本流程模块自带test_basic_flow测试完整走了一遍生命周期铸造 100/50 → 冻结双方账户 → 冻结状态下由管理者转账 10/5验证 TransferRef 可以穿透冻结→ 解冻 →withdraw提取合计 40 并deposit回去 → 最终全部销毁断言余额归零。另有test_permission_denied通过#[expected_failure(abort_code 0x50001, location Self)]验证非所有者调用会触发permission_denied。这些测试可以直接用aptos move test在 Move.toml 所在目录运行。coin_example只面向主存储的轻量发币封装coin_example.move 演示了如何把上述通用模块压扁成传统 Coin 式的单账户操作接口适合不需要多 Store 操作的场景const ASSET_SYMBOL: vectoru8 bYOLO; fun init_module(admin: signer) { let constructor_ref object::create_named_object(admin, ASSET_SYMBOL); managed_fungible_asset::initialize( constructor_ref, 0, /* maximum_supply. 0 means no maximum */ utf8(bYou only live once), /* name */ utf8(ASSET_SYMBOL), /* symbol */ 8, /* decimals */ utf8(bhttp://example.com/favicon.ico), /* icon */ utf8(bhttp://example.com), /* project */ vector[true, true, true], /* mint_ref, transfer_ref, burn_ref */ ); } #[view] public fun get_metadata(): ObjectMetadata { let metadata_address object::create_object_address(example_addr, ASSET_SYMBOL); object::address_to_objectMetadata(metadata_address) }对外暴露的 entry 函数都遵循mint(admin, to, amount)/transfer(admin, from, to, amount)/burn(admin, from, amount)/freeze_account(admin, account)/unfreeze_account(admin, account)的单账户签名内部统一包装成单元素向量调用managed_fungible_asset的主存储版本 API。get_metadata是一个#[view]函数供链下应用在不发交易的情况下反推元数据对象地址example_addr部署地址 名称种子YOLO的确定性对象地址。模块测试同样覆盖铸造 100 → 冻结发起方 → 管理者仍可转账 10再次证明 TransferRef 忽略冻结→ 解冻 → 销毁 90以及非管理者调用 mint 触发0x50001permission_denied。preminted_managed_coin铸造引用销毁后的预铸币preminted_managed_coin.move 解决一个常见问题固定供应量的代币如何在链上自证永远不会增发。const ASSET_SYMBOL: vectoru8 bMEME; const PRE_MINTED_TOTAL_SUPPLY: u64 10000; fun init_module(admin: signer) { let constructor_ref object::create_named_object(admin, ASSET_SYMBOL); managed_fungible_asset::initialize( constructor_ref, 1000000000, /* maximum_supply */ utf8(bpreminted coin), utf8(ASSET_SYMBOL), 8, utf8(bhttp://example.com/favicon.ico), utf8(bhttp://example.com), vector[false, true, true], /* mint_ref, transfer_ref, burn_ref */ ); // 创建一次性 MintRef将全部供应量预铸到储备账户 let mint_ref fungible_asset::generate_mint_ref(constructor_ref); let admin_primary_store primary_fungible_store::ensure_primary_store_exists( signer::address_of(admin), get_metadata() ); fungible_asset::mint_to(mint_ref, admin_primary_store, PRE_MINTED_TOTAL_SUPPLY); }要点有二ref_flags传入vector[false, true, true]MintRef 不存入ManagingRefs因此mint_to_primary_stores等管理铸造函数将因引用缺失ERR_MINT_REF而永远失败初始化期间临时generate_mint_ref铸造完成后该引用随交易结束而销毁——从源码结构看之后链上已不存在任何可铸造的凭据供应量被锁定在 10000。模块注释明确指出储备账户可以是普通账户、资源账户或多签账户示例用部署者账户仅为演示。其测试test_basic_flow先断言supply等于PRE_MINTED_TOTAL_SUPPLY再用#[expected_failure(abort_code 0x60004, location example_addr::managed_fungible_asset)]验证事后尝试管理铸造会失败。multisig_managed_coin用多签账户托管资产所有权multisig_managed_coin.move 展示了团队/委员会治理场景让多签账户成为元数据对象的所有者从而所有管理操作都需要多签审批。public entry fun initialize( creator: signer, additional_owners: vectoraddress, num_signature_required: u64, ... ref_flags: vectorbool, ) { let multisig_address multisig_account::get_next_multisig_account_address(signer::address_of(creator)); multisig_account::create_with_owners(creator, additional_owners, num_signature_required, ...); // 由创建者先创建元数据对象再转移给多签账户 let constructor_ref object::create_named_object(creator, *string::bytes(symbol)); object::transfer(creator, object::object_from_constructor_refObjectCore(constructor_ref), multisig_address); managed_fungible_asset::initialize(constructor_ref, maximum_supply, name, symbol, decimals, icon_uri, project_uri, ref_flags); }这里有一个值得注意的权衡源码注释原样说明了原因理想方式是直接取多签账户的 signer但当前不可用因此采用创建者先建对象、再object::transfer给多签账户的替代模式——对象所有权落在多签地址上后authorized_borrow_refs中的object::is_owner校验就自动要求多签交易才能通过实现了所有权层面的多签控制。fa_coin部署即发币的一体化模块fa_coin.moveFACoin::fa_coin把managed_fungible_asset与coin_example合二为一模块发布时即以硬编码的ASSET_SYMBOL bFA、8 位小数等参数创建资产部署者即成为管理者。与基础模块相比它额外引入两项能力暂停开关State { paused: bool }资源存入元数据对象set_pause(pauser, paused)仅元数据所有者可调用deposit/withdraw钩子中assert_not_paused暂停后一切转账经由 deposit/withdraw都会 abortEPAUSED2。可派发转账钩子dispatchable fungible asset通过dispatchable_fungible_asset::register_dispatch_functions把本模块的withdraw/deposit注册为资产的默认实现这样任何对该资产的转账最终都会执行自定义检查例如暂停状态、未来的黑名单而不只是管理函数路径let deposit function_info::new_function_info(admin, string::utf8(bfa_coin), string::utf8(bdeposit)); let withdraw function_info::new_function_info(admin, string::utf8(bfa_coin), string::utf8(bwithdraw)); dispatchable_fungible_asset::register_dispatch_functions( constructor_ref, option::some(withdraw), option::some(deposit), option::none(), );其mint函数展示了引用式管理的典型组合fungible_asset::mint(mint_ref, amount)得到内存态FungibleAsset后再用deposit_with_ref存入目标主存储。模块测试除基本流程和权限校验0x50001外还包含test_paused暂停后转账预期 abort 码2EPAUSED与常量定义一一对应。managed_fungible_token用 Token 对象承载代币身份managed_fungible_token.move 展示了 FA 与 Token Objects 框架的组合玩法先创建一个 Collectioncreate_fixed_collection和一个命名 Tokencreate_named_token再把该Token 对象本身作为constructor_ref传入managed_fungible_asset::initialize于是元数据对象地址成为 Token 对象地址——代币在对象图里同时具有非同质的身份信息Collection/名称/URI和同质化的可兑换余额。let collection_name: String utf8(btest collection name); let token_name: String utf8(btest token name); create_fixed_collection(admin, utf8(btest collection description), 1, collection_name, option::none(), utf8(bhttp://aptoslabs.com/collection)); let constructor_ref create_named_token(admin, collection_name, utf8(btest token description), token_name, option::none(), utf8(bhttp://aptoslabs.com/token)); managed_fungible_asset::initialize(constructor_ref, 0, utf8(btest fungible token), utf8(ASSET_SYMBOL), 0, utf8(bhttp://example.com/favicon.ico), utf8(bhttp://example.com), vector[true, true, true]);get_metadata的反推方式也相应变化对象地址由create_token_seed(collection_name, token_name)生成的种子计算而不是简单的ASSET_SYMBOL。该目录的 Move.toml 因此在managed_fungible_asset之外额外依赖了AptosTokenObjects0x4。stablecoin/usdk带角色与访问控制规则的稳定币参考实现stablecoin 子目录 提供了一个更完整的托管稳定币USDK参考实现其 README 列出的功能包括可升级智能合约、minter 角色制铸/销、账户拉黑denylist、合约暂停/恢复。资源结构与角色体系usdk.move 在元数据对象上挂载了三个资源struct Roles has key { master_minter: address, minters: vectoraddress, pauser: address, denylister: address, } struct Management has key { extend_ref: ExtendRef, // 供模块升级时重建元数据对象 mint_ref: MintRef, burn_ref: BurnRef, transfer_ref: TransferRef, } struct State has key { paused: bool, }Management额外保存的ExtendRef是支撑可升级能力的关键从源码结构看升级模块时可用extend_ref延长元数据对象从而保留资产身份地址不变而替换模块字节码这是稳定币这类长生命周期合约的常见诉求。转账钩子中的黑名单与暂停检查deposit/withdraw钩子在每次存取时执行assert_not_paused()与assert_not_denylisted(...)fun assert_not_denylisted(account: address) { let metadata metadata(); // 不能调用 pfs::store_exists否则会引入循环依赖 // 因此调用其内联版本 if (primary_fungible_store::primary_store_exists_inlined(account, metadata)) { assert!(!fungible_asset::is_frozen(primary_fungible_store::primary_store_inlined(account, metadata)), EDENYLISTED); } }注释特别指出了工程细节钩子内不能调用会再次进入 deposit/withdraw 的pfs::store_exists否则会形成循环依赖必须使用*_inlined版本。拉黑/解除拉黑denylist/undenylist由denylister角色执行本质是对主存储打set_frozen_flag标志并 emitDenylist事件。部署与运行该目录 Move.toml 定义了五个可注入地址stablecoin模块发布地址、master_minter、minter、pauser、denylister其 dev 环境默认值分别为0xcafe、0xbab、0xface、0xdafe、0xcade。按 stablecoin/README 给出的操作步骤README 注明当前因依赖 AIP 73 仅在 devnet 可用# 1. 创建 devnet 配置 aptos init --profile devnet # 2. 部署--named-addresses 注入五个角色地址 aptos move publish \ --named-addresses stablecoindevnet,master_minterdevnet,minterdevnet,pauserdevnet,denylisterdevnet \ --profile devnet # 3. 由 minter 角色铸造 1000000008 位小数即 1 枚 USDK aptos move run \ --function-id devnet::usdk::mint \ --args address:0x8115e523937721388acbd77027da45b1c88a6313f99615c4da4c6a32ab161b1a u64:100000000 \ --profile devnet其中接收地址需替换为实际 devnet 账户地址也可通过浏览器端模块页面调用mint函数。测试目录 usdk_tests.move 提供aptos move test的覆盖包括transfer_from基于Approval结构的签名授权转账校验nonce与chain_id防重放等函数。设计模式小结从六组示例提炼的发行范式综合整个 fungible_asset 目录可以得到以下可复用的工程经验能力即引用MintRef/TransferRef/BurnRef是能力的载体。initialize时按ref_flags选择性生成不需要的能力直接不生成如 preminted 示例不生成 MintRef比生成了再限制更安全。主存储优先create_primary_store_enabled_fungible_asset让资产余额挂在账户上用户体验与原生 Coin 一致简版 API*_to_primary_stores适合大多数业务通用 API接收vectorObjectFungibleStore留给 DeFi 等复杂托管场景。管理者转账可穿透冻结transfer_with_ref系列显式忽略frozen标志冻结只约束账户自主行为不约束管理者——这在风控追回被冻账户中的资产时是必要语义编写测试时应像示例一样专门覆盖。dispatchable 钩子是全局校验点fa_coin与usdk都通过register_dispatch_functions覆盖deposit/withdraw使暂停、黑名单等检查覆盖所有转账路径而非仅管理入口注意钩子内避免循环依赖参考assert_not_denylisted的内联写法。多签与角色分层治理个人/小团队用多签账户持有元数据对象multisig 示例规模化运营用显式Roles资源usdk 示例配合ExtendRef支持模块升级、事件系统Mint/Burn/Pause/Denylist支持链下审计。确定性地址便于链下集成get_metadata()这类#[view]函数让钱包、索引器可以离线计算资产对象地址是所有示例都保留的实用接口。以上示例均可在各自目录内用aptos move test验证逻辑用aptos move publish配合--named-addresses发布。需要说明的适用前提这些示例依赖 git 依赖拉取aptos-frameworkmainnetrev且 stablecoin 示例的部分能力如可升级依赖较新的链上特性部署前应以当前 devnet/目标网络的能力为准。【免费下载链接】aptos-coreAptos is a layer 1 blockchain built to support the widespread use of blockchain through better technology and user experience.项目地址: https://gitcode.com/GitHub_Trending/ap/aptos-core创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考