news 2026/9/10 11:26:04

fuels-ts 数值运算模块 `@fuel-ts/math` 完全指南:基于 bn.js 的安全大数运算、单位换算与格式化工具

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
fuels-ts 数值运算模块 `@fuel-ts/math` 完全指南:基于 bn.js 的安全大数运算、单位换算与格式化工具

fuels-ts 数值运算模块@fuel-ts/math完全指南:基于 bn.js 的安全大数运算、单位换算与格式化工具

【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts

@fuel-ts/math是 Fuel TypeScript SDK(fuels-ts)monorepo 中的一个基础子模块,为上层所有涉及数值运算的代码提供统一入口。它围绕decimal(十进制小数)、hex(十六进制字符串)与Uint8Array(字节数组)三类数据形态封装了一整套基于 bn.js 的数学工具,让任意大小的整数计算都足够安全、精确。读完本文,你将掌握该模块的安装方式、核心BN类型与函数式 API 的完整用法,理解formatUnits/parseUnits的单位换算机制、默认精度配置的来源,以及该模块在 SDK 内部(如账户余额、交易金额处理)的真实应用场景。

模块定位:为什么 fuels-ts 需要一个独立的数学包

在区块链场景下,绝大多数数值都远超 JavaScriptNumber的安全整数范围(Number.MAX_SAFE_INTEGER,即2^53 - 1)。无论是 Fuel 网络的资产余额、交易金额,还是燃料价格,都以最小单位(wei 级别)的整数在网络中传输。如果直接使用原生number进行计算,很容易因精度丢失而引入严重 bug。

为此,fuels-ts 单独抽取了 packages/math 作为 SDK 共用的数值底座,其设计要点是:

  • 基于 bn.js实现任意精度整数运算,无论数值多大都能安全完成加减乘除、幂、取模等计算;
  • 面向 Fuel 的实际开发场景,额外提供decimalhexUint8Array三种形态之间的互转,以及带小数位的单位格式化能力;
  • 被 SDK 内其他包大量复用。例如在 account.ts 中,余额、手续费、CoinQuantity等几乎全部用bn(...)构造、用.toNumber()/.toHex()转换,说明该模块是 SDK 数值处理的公共基础设施。

安装方式

单独使用该模块时,按官方 README 推荐使用pnpm

pnpm add @fuel-ts/math # 或 npm add @fuel-ts/math

根据 package.json,其运行时依赖仅有两个:bn.js@5.2.1及其类型声明@types/bn.js@5.1.6,外加 monorepo 内的@fuel-ts/errors(用于抛出标准化的FuelError)。安装体积与依赖面都很小,可在任意 Node.js 环境(^20 || ^22 || ^24)中使用,同时打包产物支持require/import/类型三种入口。

如果你正在开发完整的 Fuel 应用而非底层库,官方推荐直接安装聚合包fuels(SDK 的 umbrella package),它将@fuel-ts/math与地址、账户、合约、脚本、钱包等能力一并带入:

pnpm add fuels # 或 npm add fuels

导出结构总览

模块的统一出口是 index.ts,仅用五条 re-export 就组织好了全部能力:

export * from './bn'; export * from './decimal'; export * from './functional'; export * from './math'; export * from './types';

各文件的职责划分非常清晰:

源文件职责
bn.ts核心BN类(继承 bn.js 的BN)与函数式工厂bn()bn.parseUnits()
decimal.ts面向展示场景的toFixed小数格式化函数
functional.tstoNumber/toHex/toBytes/formatUnits/format五个函数式快捷调用
math.tsmaxmultiply等聚合数学函数
types.tsBigNumberishFormatConfigToFixedConfig等共享类型

核心:BN类与bn()工厂函数

统一入参类型BNInput

BN的构造与所有算术方法都接受一个宽松的入参联合类型BNInput(定义于 bn.ts):

type BNInput = number | string | number[] | Uint8Array | Buffer | BnJs;

也就是说,无论你手头是普通数字、"1000000000000000000"这样的十进制字符串、"0x1ff"十六进制字符串、字节数组,还是另一个bn.js实例,都可以直接传入参与运算,无需手动统一。

构造与输入归一化规则

BN构造函数(bn.ts)做了几件关键的输入处理:

  1. 传入BN实例时,先转成字节数组再重新构造,保证返回的是本模块的BN类型;
  2. 十六进制字符串会自动剔除0x前缀(bn.js 本身不接受该前缀),并把进制默认设为'hex'
  3. 空值保护null/undefined/缺省参数都会被当作0
  4. 安全整数校验:当入参是number且超过Number.MAX_SAFE_INTEGER时,直接抛出携带ErrorCode.NUMBER_TOO_BIGFuelError,提示“数值过大,请改用字符串”。测试 bn.test.ts 明确验证了Number.MAX_SAFE_INTEGER + 1这种不安全的 number 会被拒绝。

函数式工厂bn(value?, base?, endian?)只是new BN(...)的语法糖(bn.ts),在 SDK 内部的使用频率远高于new BN(...),例如 account.ts 中的amount: bn(fee)

核心方法速查表

方法签名行为要点
toString(base?, length?)重写后,base === 16'hex'强制带0x前缀
toHex(bytesPadding?: number)输出0x...;负数与超出 padding 长度都会抛CONVERTING_FAILED
toBytes(bytesPadding?: number)输出Uint8Array;负数抛错,支持前置补零
toJSON()输出十六进制字符串
valueOf()输出十进制字符串(与无参toString()一致)
add/sub/mul/div/pow/mod/divRound(v: BNInput)运算后返回本模块的BN,永不丢失类型引用
lt/lte/gt/gte/eq(v: BNInput)返回布尔值的比较
cmp(v: BNInput)返回-1 \| 0 \| 1
sqr/neg/abs/toTwos/fromTwos(width?)返回BN(覆盖 bn.js 原方法)
clone()深拷贝为新的BN
maxU64()若当前值超过0xFFFFFFFFFFFFFFFF,钳制到该上限
max(v: BNInput)返回两者中的较小值(语义为上限保护)
normalizeZeroToOne()若为0则返回1,否则不变
format/formatUnits见下文单位换算与展示格式化

为什么重写这么多方法:避免丢失BN引用

阅读 bn.ts 可以发现一个精心设计:addpowsub等所有算术与比较方法都经由内部caller(v, methodName)转发(bn.ts),其结果若仍是 bn.js 的BN,会立即new BN(output.toArray())重新包装成本模块的BNsqrnegabstoTwosfromTwosmulToegcddivmod也如法炮制。

这意味着你可以像下面这样无限链式调用,且每一步返回值都是本模块的BN,链式表达式的类型与行为保持一致:

bn(2).add(2).sub(2).pow('0x3').mul(2).div(2).sqr().abs().mod(2).divRound(2);

对应测试见 bn.test.ts,它逐个断言了链式中每步调用结果仍能以toString(16)得到0x前缀的十六进制串。

hex 与 Uint8Array 互转及字节对齐

Fuel 链上与交易、签名相关的很多字段都是定长字节,因此toHex(bytesPadding)/toBytes(bytesPadding)补零对齐能力至关重要。bytesPadding以字节为单位:调用toHex(8)会把值补足为 8 字节(16 个十六进制字符)宽度的0x0000000000000000形式;toBytes(8)同理返回 8 个字节的Uint8Array。若值本身超出声明的字节宽度,则抛出转换失败错误。完整边界用例可参考 bn.test.ts。

import { bn } from '@fuel-ts/math'; bn(1).toHex(); // '0x1' bn(1).toHex(2); // '0x0001' bn('0x1ff').toBytes(); // Uint8Array [1, 255] bn(255).toBytes(4); // Uint8Array [0, 0, 0, 255]

单位换算与展示格式化:format/formatUnits/parseUnits

区块链应用最常见的两类需求是:把链上的最小单位整数还原成带小数的资产展示值,以及把用户输入的带小数金额转换回最小单位整数提交上链。@fuel-ts/math为此提供了镜像的两个 API。

formatUnits(units?):整数 → 带小数位字符串

formatUnits按指定小数位在整数值上打小数点(bn.ts),默认units = 9(Fuel 网络默认 9 位小数):

bn('1000000000').formatUnits(); // '1.000000000' bn('2').formatUnits(); // '0.000000002' bn('100000020000').formatUnits(); // '100.000020000' bn('1000000000').formatUnits(7); // '100.0000000'

format(options?):带千分位、去尾零的展示格式化

formatformatUnits基础上叠加precision(最大小数位数)与minPrecision(最小保留位数),并自动添加千分位分隔符与去除尾部多余的零,是面向UI 展示的高阶封装(bn.ts):

bn('1000000000').format(); // '1.000' (默认去零后保留 3 位最小精度) bn('2').format(); // '0.000000002' bn('100000020000').format(); // '100.00002' bn('100100000020000').format(); // '100,100.00002' bn('1000000000').format({ minPrecision: 2, units: 8 }); // '10.00'

其中配置项来自 types.ts 定义的FormatConfig

type FormatConfig = { units?: number } & ToFixedConfig; type ToFixedConfig = { minPrecision?: number; precision?: number };

bn.parseUnits(value, units?):展示值 → 最小单位整数

parseUnitsformatUnits的逆操作(bn.ts),它把形如'100.00002'、甚至带千分位'100,100.00002'.的字符串解析回最小单位整数,位数不足则自动补零,小数位超过units时抛出CONVERTING_FAILED

bn.parseUnits('1'); // BN: 1000000000 bn.parseUnits('100.00002'); // BN: 100000020000 bn.parseUnits('100,100.00002', 5); // BN: 10010000002 bn.parseUnits('.'); // BN: 0 bn.parseUnits('0.000000002'); // BN: 2

默认配置的出处

以上默认值全部定义在 configs.ts,并由测试 configs.test.ts 锁定不可随意变更:

常量含义
DEFAULT_DECIMAL_UNITS9默认小数位(formatUnits/parseUnits的默认units
DEFAULT_PRECISION9默认最大展示精度
DEFAULT_MIN_PRECISION3默认最小展示精度(防止整块金额被显示成1而丢失小数视觉信息)
DECIMAL_FUEL9Fuel 网络的原生小数位数
DECIMAL_WEI/DECIMAL_KWEI/DECIMAL_MWEI/DECIMAL_GWEI18 / 15 / 12 / 9Ethereum 生态常见单位换算常数,供需要换算 ERC-20 金额的场景参考

展示辅助函数:toFixed

decimal.ts 提供的toFixed(value, options?)面向纯粹的展示场景,接受字符串或数字输入,返回带千分位分隔且小数位数被precision/minPrecision约束的字符串。它同样默认使用DEFAULT_PRECISION = 9DEFAULT_MIN_PRECISION = 3,并在minPrecision < precision时先去除尾部零再按最小精度补齐:

toFixed('100000020000'); // '100,000,020,000'(无小数输入时的整数分组) toFixed('1234567.89123'); // 千分位分组 + 精度约束 toFixed('1234.5', { precision: 4, minPrecision: 2 }); // '1,234.5'

若你只处理纯整数且不需要单位换算,用toFixed即可完成分组展示,无需构造BN实例。

函数式快捷 API 与聚合函数

为了让不持有BN实例的代码也能方便使用,functional.ts 暴露了五个一等函数,它们内部都是bn(value)的薄封装:

toNumber('0x1ff'); // 511 toHex(1, 2); // '0x0001' toBytes('0x1ff'); // Uint8Array [1, 255] formatUnits('1000000000'); // '1.000000000' format('100000020000'); // '100.00002'

math.ts 则提供两个聚合运算:

  • max(...numbers):返回一组BigNumberish中的最大值(以bn(0)为起点归约);
  • multiply(...numbers):对一组值连续相乘后向上取整。

这里的BigNumberish = string | number | BN同样来自 types.ts,是 SDK 其他模块引用本包时最常用的类型别名。

在 SDK 中的真实应用

从源码检索可以看到,@fuel-ts/math并不是孤立的工具包,而是被大量 SDK 内部代码直接依赖。以 account.ts 为例:

  • 余额与CoinQuantity的组装使用bn(0)bn(fee)bn(transferParam.amount)
  • 在计算手续费、合并输入时大量使用acc.add(input.amount)bn(0)这类链式与归约运算;
  • 十六进制编码输出时使用'0x'.concat(bn(amount).toHex()...)或直接依赖toHex()自带的0x前缀。

换言之,你在使用fuels聚合包开发 DApp 时,凡是经手余额、金额、gas 等数值的场景,底层都在使用本文介绍的BNparseUnitsformat等能力。理解了@fuel-ts/math,就等于理解了 fuels-ts 全部数值处理行为的地基。

生态与配套信息

  • 版本与依赖:包版本号见 packages/math/package.json,构建脚本为tsup,产物输出到dist
  • 变更记录:完整的版本变更历史见 packages/math/CHANGELOG.md;
  • 许可证:该子模块采用Apache 2.0,详见 packages/math/LICENSE;
  • 参与贡献:本包是 fuels-ts 单一 monorepo 的一部分,贡献指引请参见仓库根目录的 CONTRIBUTING.md,其余包对@fuel-ts/math的调用方式也可直接查阅上文提到的 account.ts 等源码作为最佳实践参考。

【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts

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

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 11:25:33

PixWit:轻量高效的开发者截图录屏工具解析

1. PixWit工具定位与核心价值程序员在日常工作中经常需要处理各种截图、录屏需求&#xff1a;可能是记录Bug现象、制作技术演示、编写文档配图&#xff0c;或是与团队成员快速共享界面状态。传统做法需要同时打开多个工具——用Snipaste截图、OBS录屏、再用剪映简单剪辑&#x…

作者头像 李华
网站建设 2026/9/10 11:25:19

Python条件判断全解析:从基础语法到实战应用

1. 程序执行顺序的真相很多小朋友刚开始学编程时&#xff0c;都会有个天真的想法&#xff1a;计算机就像听话的小学生&#xff0c;会一行一行认真读代码。但现实情况要复杂得多。让我们用个生活例子来理解&#xff1a;想象你在玩一个"如果...就..."的闯关游戏&#x…

作者头像 李华
网站建设 2026/9/10 11:23:12

MarkItDown:免费文档转 Markdown 工具,3 行代码完成集成

MarkItDown&#xff1a;免费文档转 Markdown 工具&#xff0c;3 行代码完成集成 【免费下载链接】markitdown Python tool for converting files and office documents to Markdown. 项目地址: https://gitcode.com/GitHub_Trending/ma/markitdown MarkItDown 是一个免费…

作者头像 李华
网站建设 2026/9/10 11:22:45

2026AI论文工具排行榜[特殊字符]全网实测!本科生闭眼入榜单

2026年高校重复率AIGC双审全面落地&#xff01;市面上五花八门的AI论文工具泛滥&#xff0c;要么功能单一、要么查重反噬、要么AI痕迹爆表、要么格式错乱、要么暗藏泄露风险。 为了帮大家避坑&#xff0c;全网实测8款主流热门论文AI工具&#xff0c;从综合实力、双审适配、功能…

作者头像 李华
网站建设 2026/9/10 11:22:20

基于GDAL与JTS的shp/gdb几何自相交批量修复实践

简介&#xff1a;面向GIS开发人员的Java几何拓扑修复工具类&#xff0c;基于GDAL与JTS实现&#xff0c;可检测并修复几何自相交、重叠、不闭合等拓扑错误&#xff0c;确保数据符合OGC简单要素规范&#xff0c;在geotools、PostGIS等库中稳定可用。压缩包共8个文件&#xff0c;包…

作者头像 李华