fuels-ts 地址格式转换实战:用 Address 工具类打通 B256、Contract ID、Wallet 与 Asset ID
【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts
本指南聚焦 Fuel TypeScript SDK(fuels-ts)中的地址转换问题。Fuel Network 底层统一使用 B256(64 位十六进制)地址格式,但日常开发中你还会遇到以 Struct 包裹的 Asset ID、EVM Address、Checksum 地址等多种变体。读完本文,你将掌握如何借助Address这个类型安全的工具类,在这些格式与 Sway 标准类型之间自由转换,并理解其底层实现原理。
为什么需要地址转换
任何去中心化应用的开发都离不开地址,而不同网络、不同抽象层往往强制使用不同格式的地址。在 fuels-ts 生态中,主要会遇到以下几种形态:
- B256 地址:Fuel Network 交互所使用的原生格式,本质是
0x前缀加 64 个十六进制字符,总长 66 个字符。这也是 B256 类型(Sway 内建类型)的表示:
const b256 = '0x9ae5b658754e096e4d681c548daf46354495a437cc61492599e33fc64dcdc30c';- Hex 格式字符串:不带任何业务语义的裸十六进制串,同样常见。
- Struct 包裹类型:例如 Asset ID 与 EVM Address,它们在 Sway 中是以
Struct形式包裹(对应 TypeScript 侧形如{ bits: B256 }的对象),参见 Struct 类型说明。
fuels-ts 通过 Address 工具类 让这些格式之间的转换变得简单——它提供了一系列转换与比较工具方法。下文将逐一演示如何利用该类在地址格式与 Sway 标准类型之间完成互转。
Address 类:一切转换的核心
在 Sway 中,Address是对内建B256原始类型的类型安全封装;而 fuels-ts 的 SDK 另有一套自己的Address抽象。其实现位于 packages/address/src/address.ts,类的核心只有一个只读属性:
readonly b256Address: B256Address;即所有地址最终都会统一归一化并存储为小写的 B256 地址(见 normalizeB256)。围绕这一个属性,类提供了完整的方法族(源码见 packages/address/src/address.ts):
| 实例方法 | 返回类型 | 作用 |
|---|---|---|
toAddress() | B256Address | 返回b256Address属性(L46-L48) |
toB256() | B256Address | 返回 B256 哈希地址字符串(L55-L57) |
toBytes() | Uint8Array | 返回 B256 地址的字节数组(L64-L66) |
toHexString() | B256Address | 等价于toB256()(L73-L75) |
toEvmAddress() | EvmAddress | 转为 SwayEvmAddress的 Struct 形态(L99-L103) |
toAssetId() | AssetId | 包装为 SwayAssetId的 Struct 形态(L109-L113) |
toChecksum() | ChecksumAddress | 生成 ERC-55 风格的校验和地址(L39-L41) |
toString()/valueOf() | string | 均返回校验和地址字符串(L82-L84、L119-L121) |
equals(other) | boolean | 比较两个地址是否相等(L128-L130) |
构造函数的“万能输入”机制
new Address(address)的构造函数接受多种输入形态并自动判别(L28-L31):传入一个 B256 字符串、一个 512 位公钥字符串、一个 EVM 地址字符串,或一个已有的Address实例均可。其分发逻辑集中在 fromDynamicInputToB256:
- 若传入对象实现了
toB256(),直接取用; - 否则按长度与正则依次判断是否为B256(
0x+ 64 位 hex,isB256); - 再判断是否为公钥(
0x+ 128 位 hex,即 512 bit,isPublicKey),是则对其做 sha256 哈希得到 B256; - 接着判断是否为EVM 地址(
0x+ 40 位 hex,即 20 字节,isEvmAddress),是则通过 fromEvmAddressToB256 在头部补齐 12 字节零值得到 B256; - 都不匹配则抛出
FuelError(错误码PARSE_FAILED)。
需要注意,地址字符串在比较时会被统一转小写(normalizeB256),这意味着大小写差异不会影响相等性判断;而若需要带校验能力的表示,则应使用toChecksum()返回的 ERC-55 校验和地址。
转换 Contract ID
合约的id属性是一个Address实例(文档参见 address-conversion.md)。因此可以直接复用Address类的toAddress、toB256等方法完成转换。下面是从一个已部署合约获取其 B256 地址的完整示例(代码来自 contract.ts):
import type { B256Address } from 'fuels'; import { Address, Provider, Contract } from 'fuels'; import { LOCAL_NETWORK_URL } from '../../../../env'; import { Counter } from '../../../../typegend/contracts'; const provider = new Provider(LOCAL_NETWORK_URL); const contractAbi = Counter.abi; const contractAddress = new Address( '0x6d309766c0f1c6f103d147b287fabecaedd31beb180d45cf1bf7d88397aecc6f' ); const contract = new Contract(contractAddress, contractAbi, provider); const b256: B256Address = contract.id.toAddress(); // 0x6d309766c0f1c6f103d147b287fabecaedd31beb180d45cf1bf7d88397aecc6f其中contract.id的类型即为Address,所以contract.id.toAddress()与contract.id.toB256()均可直接调用,二者等价。这里通过Address的构造函数直接创建合约地址(而非依赖 typegen 生成的常量),充分体现了new Address(...)可接受原始 B256 字符串的能力。
转换 Wallet Address
同理,钱包的address属性同样是Address类型,因此可调用完全相同的方法族。下面的示例先用 B256 构造地址,再创建钱包对象并取出其 B256 地址(代码来自 wallet.ts):
import type { B256Address, WalletLocked } from 'fuels'; import { Address, Provider, Wallet } from 'fuels'; import { LOCAL_NETWORK_URL } from '../../../../env'; const provider = new Provider(LOCAL_NETWORK_URL); const address = new Address( '0x6d309766c0f1c6f103d147b287fabecaedd31beb180d45cf1bf7d88397aecc6f' ); const wallet: WalletLocked = Wallet.fromAddress(address, provider); const b256: B256Address = wallet.address.toAddress(); // 0x6d309766c0f1c6f103d147b287fabecaedd31beb180d45cf1bf7d88397aecc6f这里通过Wallet.fromAddress(address, provider)把Address实例传入钱包构造器;钱包的address属性保持Address类型,因此wallet.address.toAddress()、wallet.address.toB256()、wallet.address.toEvmAddress()等方法都能直接使用。当你需要把一个地址作为交易收款方、查询余额等场景前,都可先用这些方法统一转成需要的目标格式。
转换 Asset ID
Asset ID 是包裹了内层 B256 值的 Struct 类型(对应 Sway 标准库定义,TypeScript 表示为{ bits: string },参见 asset-id.md)。下面的例子展示了如何从一个 B256 构造Address,再转成AssetId(代码来自 asset-id.ts):
import type { AssetId, B256Address } from 'fuels'; import { Address } from 'fuels'; const b256: B256Address = '0x6d309766c0f1c6f103d147b287fabecaedd31beb180d45cf1bf7d88397aecc6f'; const address: Address = new Address(b256); const assetId: AssetId = address.toAssetId(); // { // bits: '0x6d309766c0f1c6f103d147b287fabecaedd31beb180d45cf1bf7d88397aecc6f' // }可以看到toAssetId()只是把内部的b256Address包装进{ bits }结构(实现见 toAssetId),因此这个资产 ID 在链上与原来的 B256 字节完全一致。反过来,当你的合约函数接收一个AssetId参数时,也可以把assetId.bits拿出来直接构造new Address(assetId.bits),进而复用本类全部方法。
转换到 EVM Address 与校验和地址
除上述官方文档演示的场景外,Address还提供了与跨链/校验场景强相关的两个实用方向:
EVM Address:toEvmAddress()返回 SwayEvmAddressStruct({ bits: ... })。其底层 toB256AddressEvm 会先校验输入为合法 B256,然后裁掉前 12 字节、再在头部补回 12 个零字节,得到低位 20 字节与 EVM 地址对应的 B256。反之,若你手里是标准 20 字节的 EVM 地址字符串,new Address(evmAddress)会自动通过 fromEvmAddressToB256 补齐为 B256,无需手动 pad。
Checksum(校验和)地址:toChecksum()遵循 ERC-55 规范——对 B256 十六进制串做 sha256 后,按哈希位逐位决定原字符是否大写(实现见 address.ts 的私有 toChecksum)。toString()、valueOf()返回的正是这种校验和形式,equals()在内部也基于校验和比较(L128-L130),因此它同时具备“展示带校验、比较不受大小写干扰”的双重能力。
底层原理小结:一次构造,处处可转
纵观 packages/address/src/address.ts 的实现,fuels-ts 的地址转换设计可以概括为一句话:所有格式在构造Address时被统一折叠为一个小写的 B256,之后任何格式的输出都只由这一个事实源派生。
- 输入归一:构造函数经由 fromDynamicInputToB256 自动识别 B256 / 公钥(512) / EVM 地址 / 已有
Address,无法识别时抛错,避免静默产生错误地址。 - 输出派生:
toAddress/toB256/toHexString直接返回内部值;toBytes做字节化;toEvmAddress/toAssetId做 Struct 包装;toChecksum/toString做 ERC-55 校验编码。
这种“单一内部表示 + 双向可转换”的设计,使你在处理合约id、钱包address、链上 Asset ID 以及跨链 EVM 地址时,永远只需要一份标准化的转换心智模型。完整入口文档见 address-conversion.md,Address 类型的整体介绍见 address.md,b256.md 与 evm-address.md 则分别给出了各目标类型在 Sway 侧的语义定义,可对照阅读。
【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考