首页
/ fuels-ts 中的 Asset ID 机制:getMintedAssetId 与 createAssetId 原理及实战

fuels-ts 中的 Asset ID 机制:getMintedAssetId 与 createAssetId 原理及实战

2026-09-05 14:55:37作者:伍希望

在 Fuel 网络中,每种资产都由一个唯一的 Asset ID 标识,而资产发行(Mint)合约在铸造代币时必须先搞清楚:这个新资产的 ID 到底是什么?本文基于 fuels-ts 官方文档 Minted Token Asset ID,完整讲解 Asset ID 的推导规则、getMintedAssetIdcreateAssetId 两个核心工具函数,并结合仓库源码剖析其底层哈希实现与在交易回执(Receipt)处理中的实际调用位置,帮助你在部署代币合约后正确计算、使用铸造出的资产 ID。

Asset ID 由什么决定

Fuel 网络上一个代币的 Asset ID 由两个因素共同决定:

  • 铸造该代币的合约 ID(Contract ID)
  • 子标识符(Sub ID)

两者都是 B256 字符串(32 字节,64 个十六进制字符加 0x 前缀)。

推导过程是把 Contract ID 与 Sub ID 拼接后对其应用 SHA-256 哈希算法,得到的哈希值即为该资产的 Asset ID。这意味着:

  • Contract ID 是动态的——每次部署合约,生成的合约 ID 都不同,因此同一份合约代码部署两次会产生两种不同的资产;
  • Sub ID 可以是固定的——它只是合约 ABI 方法里的一个普通 b256 参数,开发者可以约定一个常量值。

这一特性让合约天然支持"多资产发行":同一个合约用不同的 Sub ID 铸造,就会得到互不冲突的多个 Asset ID。

一个简化的 Token 合约

先看文档中给出的这个简化版代币合约(token/src/main.sw),它是理解后续操作的上下文:

contract;

use std::asset::{burn, mint, transfer};

abi Token {
    fn transfer_to_address(target: Address, asset_id: AssetId, coins: u64);
    fn transfer_to_contract(recipient: ContractId, asset_id: AssetId, coins: u64);
    fn mint_coins(sub_id: b256, mint_amount: u64);
    fn burn_coins(sub_id: b256, burn_amount: u64);
}

impl Token for Contract {
    fn transfer_to_address(recipient: Address, asset_id: AssetId, amount: u64) {
        transfer(Identity::Address(recipient), asset_id, amount);
    }

    fn transfer_to_contract(target: ContractId, asset_id: AssetId, amount: u64) {
        transfer(Identity::ContractId(target), asset_id, amount);
    }

    fn mint_coins(sub_id: b256, mint_amount: u64) {
        mint(sub_id, mint_amount);
    }

    fn burn_coins(sub_id: b256, burn_amount: u64) {
        burn(sub_id, burn_amount);
    }
}

合约中真正执行铸造的是 Sway 标准库的 mint(sub_id, amount) 内建函数。注意 sub_id 是每次调用时传入的参数,而不是合约常量——这决定了 Asset ID 只有在合约部署完成后、选定 Sub ID 之后才能被计算出来。

部署合约并铸造代币

假设上述合约已经通过 typegen 生成了 TokenFactory,文档给出的铸造流程如下(minted-token-asset-id.ts):

import { bn, getMintedAssetId, Provider, Wallet } from 'fuels';

import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from '../../../../env';
import { TokenFactory } from '../../../../typegend';

const provider = new Provider(LOCAL_NETWORK_URL);
const deployer = Wallet.fromPrivateKey(WALLET_PVT_KEY, provider);

const deployContract = await TokenFactory.deploy(deployer);
const { contract } = await deployContract.waitForResult();

// Any valid B256 string can be used as a sub ID
const subID =
  '0xc7fd1d987ada439fc085cfa3c49416cf2b504ac50151e3c2335d60595cb90745';
const mintAmount = bn(1000);

const { waitForResult } = await contract.functions
  .mint_coins(subID, mintAmount)
  .call();
await waitForResult();

// Get the minted
const mintedAssetId = getMintedAssetId(contract.id.toB256(), subID);

console.log('Minted asset ID should be defined', mintedAssetId);
const { transactionResult } = await waitForResult();
console.log(
  'Transaction should be successful',
  transactionResult.isStatusSuccess
);

几个关键点:

  1. Sub ID 可以是任意合法的 B256 字符串。示例中用一个写死的 B256 值演示;生产中常见做法是把 Sub ID 设为合约代码中约定的常量,便于团队统一。
  2. contract.id.toB256() 返回的是部署后动态生成的合约 ID 的 B256 字符串形式,这正是 Asset ID 推导中"动态"的那一半。
  3. getMintedAssetId 是一个纯本地计算:它不访问节点、不消耗 gas,任何时候只要知道 Contract ID 和 Sub ID,就能在客户端直接算出 Asset ID。这一点在"部署后立即转账"或"预估手续费"等场景中非常有用——你甚至可以在 mint 交易上链之前就拿到未来的 Asset ID。

getMintedAssetId 的源码实现

getMintedAssetId 定义在 receipt.ts@fuel-ts/transactions 包,经由 fuels 聚合包导出):

export const getMintedAssetId = (contractId: string, subId: string): string => {
  const contractIdBytes = arrayify(contractId);
  const subIdBytes = arrayify(subId);

  return sha256(concat([contractIdBytes, subIdBytes]));
};

实现非常直白:

  • arrayify(来自 @fuel-ts/utils)把两个 B256 十六进制字符串各自转成 32 字节的 Uint8Array
  • concat 将 Contract ID 的 32 字节拼在 Sub ID 的 32 字节前面,得到 64 字节输入;
  • 最后用 @noble/hashessha256 求哈希,返回 32 字节结果的十六进制字符串。

字节拼接顺序(Contract ID 在前、Sub ID 在后)是协议层面的规定,顺序颠倒会得到完全不同的 Asset ID。从源码结构看,这个函数没有任何输入校验,因此调用方有责任保证传入的是合法的 B256 字符串(否则 arrayify 会因非法十六进制而抛出异常)。

由于 Asset ID 取决于动态的 Contract ID,而 Sub ID 可以固定,这个辅助函数就是文档推荐用来"给定合约 ID 和 Sub ID 快速求得 Asset ID"的官方入口。

createAssetId:直接获得 Sway 原生的 AssetId 参数

如果你要在调用 Sway 方法时把 Asset ID 作为参数传入(例如 transfer_to_address(recipient, asset_id, amount) 中的 asset_id),需要的是 Sway 原生类型 AssetId 对应的 JS 对象,而不是普通字符串。SDK 提供的 createAssetId 就是为此设计的,它内部直接复用 getMintedAssetIdreceipt.ts):

export const createAssetId = (contractId: string, subId: string): AssetId => ({
  bits: getMintedAssetId(contractId, subId),
});

AssetId@fuel-ts/address 包中的类型别名,本质是形如 { bits: string } 的 B256 包装对象,与 Sway 端的 AssetId 结构体一一对应,可直接作为合约调用参数。文档中的使用示例(create-asset-id.ts):

import type { AssetId, B256Address } from 'fuels';
import { createAssetId } from 'fuels';

const contractId: B256Address =
  '0x67eb6a384151a30e162c26d2f3e81ca2023dfa1041000210caed42ead32d63c0';
const subID: B256Address =
  '0xc7fd1d987ada439fc085cfa3c49416cf2b504ac50151e3c2335d60595cb90745';

const assetId: AssetId = createAssetId(contractId, subID);
// {
//   bits: '0x16c1cb95e999d0c74806f97643af158e821a0063a0c8ea61183bad2497b57478'
// }

对同一对 (contractId, subID)createAssetIdgetMintedAssetId 的哈希结果完全一致,区别只在于返回值类型:前者返回可直接喂给合约调用的 AssetId 对象,后者返回裸的 B256 字符串,适合日志输出、比较或存储。

它不只是工具函数:SDK 内部也在用它

从源码结构看,getMintedAssetId 除了供开发者手动计算 Asset ID 外,还被 SDK 内部用于解析交易回执。在 serialization.ts@fuel-ts/account 包的 providers 工具模块)中,SDK 在把节点返回的 ReceiptMint / ReceiptBurn 回执序列化为强类型对象时,会用 getMintedAssetId(contractId, subId) 由回执中携带的合约 ID 和 Sub ID 还原出对应的 Asset ID。这解释了为什么文档示例里 mint 交易完成后、无需额外查询即可拿到"本次铸造的 Asset ID"——它与节点侧的推导逻辑是同一个公式,天然一致。

小结与适用边界

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
528
588
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
906
1.83 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
docsdocs
暂无描述
Markdown
891
5.79 K
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.53 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.34 K
1.45 K
cann-learning-hubcann-learning-hub
CANN 学习中心仓,支持在线互动运行、边学边练,提供教程、示例与优化方案,一站式助力昇腾开发者快速上手。
Jupyter Notebook
988
506
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384