Sway 智能合约 Mint to Address:向地址铸造资产的完整指南
【免费下载链接】sway🌴 Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway
铸造(Mint)是 Sway 智能合约中最核心的资产操作之一。本文以官方参考文档 Mint to Address 为骨架,讲解如何在 Fuel 链上使用标准库std::asset::mint_to铸造指定数量的资产并将其直接转移给一个Address(账户地址)。读完本文,你将掌握mint_to的导入方式、参数语义、Address/Identity/SubId三个关键类型的用法,以及该函数在 sway-lib-std/src/asset.sw 中从mint指令到transfer转移的完整底层调用链,并了解铸造到地址时的回滚(revert)条件与安全注意事项。
一、核心概念:Address、Identity 与 SubId
在讲解具体代码之前,需要先理清三个与铸造地址密切相关的类型。
1.Address:UTXO 模型中的输出地址
在 Fuel 的 UTXO 模型中,每一个输出(output)都对应一个地址。Address是一个包含b256值的结构体,其官方定义如下(见 namespace 代码锚点):
pub struct Address { bits: b256, }Address的值是对以下二者之一的哈希:
- 一个公钥(public key)
- 一个 Predicate(谓词脚本)
需要注意:Address与ContractId是完全分离的两种类型,不应用Address来表示一个已部署合约的地址。
Address与b256之间可以互相转换:
let variable1 = 0x000000000000000000000000000000000000000000000000000000000000002A; let my_address = Address::from(variable1); let variable2: b256 = my_address.into(); // variable1 == variable22.Identity:统一处理地址与合约的统一枚举
mint_to的接收方参数类型是Identity,而不是Address。Identity是一个枚举,其定义见 sway-lib-std/src/identity.sw:
pub enum Identity { Address: Address, ContractId: ContractId, }Identity的作用是让开发者能够以统一的方式处理"转移给地址"与"转移给合约"两类场景。当我们要铸造给某个Address时,就用Identity::Address(user)将其包装起来。
3.SubId:子标识符
SubId(sub identifier)用于在同一合约下区分不同的资产。同一合约通过ContractId::this()与不同的SubId组合,可以派生出不同的AssetId。铸造时通常使用SubId::zero()表示默认子标识。
二、导入mint_to函数
mint_to位于标准库的资产模块中。使用前需要显式导入:
use std::asset::mint_to;此导入语句与文档中的mint_to_import锚点一致,见 asset_operations 示例代码。
三、铸造到地址:完整示例
铸造资产并发送到指定Address时,需要指定两个关键参数:
amount:希望铸造的数量;- 接收方:封装为
Identity::Address的目标地址。
以下是文档与示例代码(asset_operations 示例代码)中的完整写法:
fn minting_to_address() { let amount = 10; let address = 0x0000000000000000000000000000000000000000000000000000000000000001; let user = Address::from(address); mint_to(Identity::Address(user), SubId::zero(), amount); }逐行解读:
let amount = 10;—— 定义铸造数量为 10。let address = 0x...0001;—— 定义一个b256字面量作为目标地址。let user = Address::from(address);—— 将b256转换为Address类型。mint_to(Identity::Address(user), SubId::zero(), amount);—— 以Identity::Address包装user,以SubId::zero()作为子标识,铸造amount数量的资产并转移给该地址。
参数签名与含义
根据 sway-lib-std/src/asset.sw 中的定义,mint_to的完整签名为:
pub fn mint_to(to: Identity, sub_id: SubId, amount: u64)| 参数 | 类型 | 含义 |
|---|---|---|
to | Identity | 接收资产的账户身份,可以是Identity::Address(addr)或Identity::ContractId(id) |
sub_id | SubId | 要铸造的资产的子标识符 |
amount | u64 | 铸造的币数量 |
四、底层实现剖析:从mint到transfer的两步调用链
mint_to之所以能做到"铸造并转移",是因为它在内部执行了两次操作。源码实现如下(sway-lib-std/src/asset.sw):
pub fn mint_to(to: Identity, sub_id: SubId, amount: u64) { mint(sub_id, amount); transfer(to, AssetId::new(ContractId::this(), sub_id), amount); }第一步:mint—— 通过 Sway 汇编指令铸造
mint将资产铸造到当前合约自身的余额中,其实现直接调用了 Fuel 虚拟机(VM)的mint指令(sway-lib-std/src/asset.sw):
pub fn mint(sub_id: SubId, amount: u64) { asm(r1: amount, r2: sub_id) { mint r1 r2; } }即把amount放入寄存器r1、sub_id放入r2,执行mint r1 r2指令。此时新铸造的资产由当前合约持有。
第二步:transfer—— 将资产转移给目标地址
紧接着,mint_to构造资产 IDAssetId::new(ContractId::this(), sub_id)并调用transfer把刚刚铸造出的资产从合约余额转移给目标Identity。transfer内部按接收方类型分派(sway-lib-std/src/asset.sw):
pub fn transfer(to: Identity, asset_id: AssetId, amount: u64) { match to { Identity::Address(addr) => transfer_to_address(addr, asset_id, amount), Identity::ContractId(id) => force_transfer_to_contract(id, asset_id, amount), }; }当目标是Address时,走transfer_to_address分支:该函数会遍历当前交易的所有输出(outputs),寻找一个金额为 0 的未使用OutputVariable,找到后通过tro指令完成转移(sway-lib-std/src/asset.sw):
fn transfer_to_address(to: Address, asset_id: AssetId, amount: u64) { let mut index = 0; let number_of_outputs = output_count().as_u64(); while index < number_of_outputs { if let Some(Output::Variable) = output_type(index) { if let Some(0) = output_amount(index) { asm(r1: to.bits(), r2: index, r3: amount, r4: asset_id) { tro r1 r2 r3 r4; }; return; } } index += 1; } revert(FAILED_TRANSFER_TO_ADDRESS_SIGNAL); }由此可见:铸造到地址的本质是"先铸造到合约,再通过tro指令转账到地址"。这也解释了为何"铸造到合约"(Identity::ContractId)与"铸造到地址"在底层走的是不同的转移路径。
五、回滚(Revert)条件与安全注意事项
从源码文档(doc comments)与实现中可以确认以下回滚条件:
transfer到地址时(sway-lib-std/src/asset.sw):- 当
amount大于合约中asset_id对应余额时回滚; - 当
amount等于 0 时回滚; - 当交易中没有可用的空闲可变输出(free variable output)时回滚(即上面
transfer_to_address中遍历后未找到未使用输出,调用revert(FAILED_TRANSFER_TO_ADDRESS_SIGNAL))。
- 当
transfer到合约时(force_transfer_to_contract,sway-lib-std/src/asset.sw):当amount大于合约余额或等于 0 时回滚。
⚠️ 特别警示:永久丢失资产的风险
mint_to的文档注释明确警告(sway-lib-std/src/asset.sw):
如果
to是一个合约,即使接收合约没有任何可取回资金的机制(例如没有提款功能),资产也会被转移过去,若使用不当可能导致资产的永久丢失(PERMANENT LOSS OF COINS)。
虽然本文主题是铸造到Address(账户地址),但在使用mint_to设计业务逻辑时,务必意识到同一函数也支持Identity::ContractId,需要根据接收方类型谨慎设计。
六、与其他铸造方式的关系
在 铸造总览文档 中,标准库sway-lib-std/src/asset.sw提供了两种铸造函数:
mint():只铸造,资产留在当前合约余额中,不进行转移:use std::asset::mint; let amount = 10; mint(SubId::zero(), amount);mint_to():铸造并转移,可指定Identity::Address(本文场景)或Identity::ContractId。
完整的铸造到地址示例代码还包含铸造到合约(minting_to_contract)与同时铸造到两类目标(minting_to)的写法,均可参考 asset_operations 示例代码。
七、实践建议
- 明确目标类型:向账户地址铸造时使用
Identity::Address(Address::from(b256));向合约铸造时使用Identity::ContractId,不要混用Address与ContractId。 - 留意 SubId 与资产 ID:
mint_to中使用的sub_id与最终资产 ID 的关系是AssetId::new(ContractId::this(), sub_id),同一合约可借助不同SubId铸造多种资产。 - 预留可变输出:由于铸造到地址依赖
tro指令写入可变输出,交易必须包含足够的可变输出数量,否则会以FAILED_TRANSFER_TO_ADDRESS_SIGNAL回滚。 - 金额与余额校验:确保
amount大于 0 且不超过合约在该资产上的余额,避免触发回滚。
如需深入了解Address的底层定义、ContractId以及Identity的其他辅助方法,可继续阅读 Address 参考文档、ContractId 参考文档 与 Identity 源码。
【免费下载链接】sway🌴 Empowering everyone to build reliable and efficient smart contracts.项目地址: https://gitcode.com/GitHub_Trending/sw/sway
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考