欢迎光临
我们一直在努力

Sway 资产转移指南:使用 transfer 函数向地址或合约发送资产

Sway 资产转移指南:使用 transfer 函数向地址或合约发送资产

【免费下载链接】sway 🌴 Empowering everyone to build reliable and efficient smart contracts. 【免费下载链接】sway 项目地址: https://gitcode.com/GitHub_Trending/sw/sway

本篇技术指南聚焦 Sway 语言(Fuel 生态的智能合约编程语言)标准库中最常用的资产操作之一——通过 std::asset::transfer 函数将资产(如原生资产、合约自定义资产)从一个主体转移到另一个主体。文中将以仓库 docs/reference/src/documentation/operations/asset/transfer/address-or-contract.md 为骨架,结合 sway-lib-std/src/asset.sw 的源码实现与 examples/native_asset 实战示例,讲解如何同时兼容"转账到地址"与"转账到合约"两种目标,帮助你掌握可复制的转账代码模板,并理解底层指令、回退条件与潜在风险。

一、为什么需要统一的转账接口:Identity 枚举

在 Sway 的资产模型中,资产的接收方可能是两类实体:

  • 地址(Address):代表外部账户(用户钱包),例如 0x0000…0001;
  • 合约(ContractId):代表链上合约,例如去中心化交易所的流动性池合约。

标准库通过 Identity 枚举把这两类实体统一起来,让 transfer 只用一套函数签名即可覆盖两种场景。从源码看,Identity 是一个带标签的枚举:

pub enum Identity {
Address: Address,
ContractId: ContractId,
}

正是基于这个枚举,transfer 才能在运行时判断目标类型并自动选择对应的底层转移指令。

二、导入 transfer 函数

要使用 transfer,第一步是从标准库导入它。原文档给出的导入语句为(对应 lib.sw 中的 transfer_import 锚点):

use std::asset::transfer;

在合约场景中,通常还会一并导入 Identity、Address、ContractId 与 AssetId 类型,例如官方示例 examples/native_asset/src/main.sw 的写法:

use std::{asset::*, call_frames::msg_asset_id, constants::DEFAULT_SUB_ID, context::*};

三、核心用法:同时支持"转到地址"与"转到合约"

原文档 address-or-contract.md 明确指出:要转移一定数量的资产,需要指定三个参数——

  • amount:希望转移的数量;
  • asset:要转移的资产(AssetId,例如 AssetId::base() 表示原生资产,或用 AssetId::new(contract_id, sub_id) 指定自定义资产);
  • Identity:接收方的统一身份(Identity::Address(…) 或 Identity::ContractId(…))。
  • 完整代码示例如下(对应 lib.sw 中的 transfer 锚点):

    fn transferring_to() {
    let amount = 10;
    let address = 0x0000000000000000000000000000000000000000000000000000000000000001;
    let asset = AssetId::base();
    let user = Identity::Address(Address::from(address));
    let pool = Identity::ContractId(ContractId::from(address));

    transfer(user, asset, amount);
    transfer(pool, asset, amount);
    }

    这段代码演示了两个要点:

    • 向地址转账:先用 Address::from(address) 把 b256 字面量包装成 Address,再包成 Identity::Address(user);
    • 向合约转账:用 ContractId::from(address) 构造 ContractId,再包成 Identity::ContractId(pool);
    • 同一个 transfer(user, asset, amount) 调用即可覆盖两类目标,这正是"address-or-contract"命名(也是本指南标题)的含义。

    在实际合约中,通常把这一逻辑封装为 ABI 方法。参考 examples/native_asset/src/main.sw 中的 transfer_coins:

    abi NativeAsset {
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity);
    }

    impl NativeAsset for Contract {
    /// Transfer coins to a target contract.
    fn transfer_coins(coins: u64, asset_id: AssetId, target: Identity) {
    transfer(target, asset_id, coins);
    }
    }

    注意:接收方为外部地址时,转账会消耗一个未使用的变量输出(variable output);若交易没有预留空闲的变量输出,转账会失败(详见下文第四节)。

    四、底层实现:transfer 如何分派到两种目标

    transfer 并非一条魔法指令,而是对两类底层转移指令的封装。阅读 sway-lib-std/src/asset.sw 的 transfer 函数(L125-L130):

    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),
    };
    }

    可以看到:

    • 当目标是 Identity::Address 时,内部调用私有函数 transfer_to_address,底层对应汇编指令 tro(transfer output),它会寻找一个空闲的变量输出并写入转账信息;
    • 当目标是 Identity::ContractId 时,内部调用私有函数 force_transfer_to_contract,底层对应汇编指令 tr(transfer to contract),直接把资产无条件转入目标合约余额。

    这一设计意味着:向合约转账没有任何"接收回调"或"同意机制",纯粹由发送方发起,这与向地址转账的变量输出机制有本质区别。

    五、向地址转账:变量输出(variable output)机制

    从 asset.sw 的 transfer_to_address 实现(L189-L210)可以看到向地址转账的关键逻辑:

    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);
    }

    实现要点如下:

    • 遍历交易的输出列表(output_count / output_type / output_amount 均来自标准库 outputs.sw);
    • 寻找一个类型为 Output::Variable 且金额为 0 的输出——金额为 0 的变量输出可视为"未使用",因为向输出转账 0 金额会触发 panic;
    • 找到后执行 tro 指令,把 amount 数量的 asset_id 转入指定地址,并立即返回;
    • 如果遍历完都没有可用的变量输出,则调用 revert(FAILED_TRANSFER_TO_ADDRESS_SIGNAL) 回滚。该错误信号定义在 error_signals.sw:

    pub const FAILED_TRANSFER_TO_ADDRESS_SIGNAL = 0xffff_ffff_ffff_0001;

    因此,调用方向地址转账前必须确保交易中预留了空闲的变量输出(在 Fuel 客户端构造交易时配置),否则调用会以该信号回退。

    六、向合约转账:无条件转移与资产永久丢失风险

    向合约转账走的是另一条路径。force_transfer_to_contract 的实现(asset.sw L159-L163):

    fn force_transfer_to_contract(to: ContractId, asset_id: AssetId, amount: u64) {
    asm(r1: amount, r2: asset_id, r3: to.bits()) {
    tr r3 r1 r2;
    }
    }

    函数名中的 force 非常关键,标准库文档和原文档都对此给出了明确警示:如果接收方合约没有提供取款(withdrawal)能力,向该合约转移的资产将永久丢失。transfer 的文档注释(asset.sw L95-L124)用加粗形式强调了这一点:

    If the to Identity is a contract this may transfer coins to the contract even with no way to retrieve them (i.e. no withdrawal functionality on receiving contract), possibly leading to the PERMANENT LOSS OF COINS if not used with care.

    所以在把资产转给合约之前,开发者必须确认目标合约实现了对应的资产取回接口(例如 withdraw 类方法),或该合约具备处理转入资产的能力(如流动性池的充值逻辑)。

    七、回退条件(Reverts)与注意事项汇总

    综合 asset.sw 中文档注释,transfer 及其底层函数在以下情况会回退:

    场景说明
    余额不足 当前合约对 asset_id 的余额小于 amount 时回退
    金额为零 amount 等于 0 时回退
    缺少变量输出 向地址转账时,若交易没有空闲的变量输出,以 FAILED_TRANSFER_TO_ADDRESS_SIGNAL(0xffff_ffff_ffff_0001)回退
    接收方无取款能力 向合约转账时若对方无法取回,资产可能永久丢失(不触发回退,但属于资金安全隐患)

    最佳实践建议:

    • 转账前先用 this_balance(asset_id) 或 balance_of(contract_id, asset_id)(见 examples/native_asset/src/main.sw)校验余额;
    • 向地址转账时,确保客户端构造交易时预留变量输出;
    • 向合约转账前,确认对方合约具备资产取回/处理能力,必要时先审查其 ABI;
    • 涉及资产入账的合约方法需标注 #[payable],否则通过 transfer 转入的资产可能导致调用被拒(参见 native_asset 示例 中的 deposit 方法)。

    八、延伸阅读

    本文聚焦"地址或合约"这一统一入口,仓库文档中还有两篇针对单一目标的展开说明:

    • 向地址转账的专门实现:聚焦 Identity::Address 场景;
    • 向合约转账的专门实现:聚焦 Identity::ContractId 场景;
    • 资产操作总览:涵盖 mint、burn、transfer、balance 等全套标准库资产 API;
    • 标准库资产模块源码:mint、mint_to、burn、transfer 的完整实现与文档注释;
    • Identity 类型定义:地址与合约的统一封装及辅助方法(as_address、is_contract_id、bits 等);
    • 资产操作示例工程:完整可编译的合约示例,展示了 transfer、mint_to、balance_of 的组合用法。

    【免费下载链接】sway 🌴 Empowering everyone to build reliable and efficient smart contracts. 【免费下载链接】sway 项目地址: https://gitcode.com/GitHub_Trending/sw/sway

    创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

    赞(0)
    未经允许不得转载:171主机测评 » Sway 资产转移指南:使用 transfer 函数向地址或合约发送资产
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址