首页
/ fuels-ts 类型生成实战:fuels typegen 为 Sway 合约、脚本与谓词生成强类型 API

fuels-ts 类型生成实战:fuels typegen 为 Sway 合约、脚本与谓词生成强类型 API

2026-09-05 12:52:30作者:裘旻烁

本文以 demo-typegen 示例 为核心,讲解 fuels-ts 仓库中 fuels typegen 类型生成(Typegen)的完整工作流:如何从 Sway 合约、脚本(script)与谓词(predicate)编译产物中提取 ABI,生成 TypeScript 强类型客户端,并基于生成的类型完成部署、调用与转账等端到端操作。读完本篇,你可以独立在自己的项目里配置 forc buildfuels typegen 流水线,理解 --script--predicate 等参数的作用,并掌握生成类型的实际调用方式。

一、demo-typegen 示例的结构

apps/demo-typegen 是 fuels-ts 仓库中一个独立的最小示例包,其 README 描述了它的基本组成:一个 Sway 程序加一个 TypeScript 项目。结合仓库实际目录,当前示例已扩展为三种 Sway 程序类型各一个,外加 TypeScript 侧的测试与消费代码:

apps/demo-typegen/
├── demo-contract/
│   ├── Forc.toml          # Sway 项目清单
│   └── src/main.sw        # Sway 合约
├── demo-script/
│   ├── Forc.toml
│   └── src/main.sw        # Sway 脚本
├── demo-predicate/
│   ├── Forc.toml
│   └── src/main.sw        # Sway 谓词
├── src/
│   └── demo.test.ts       # 消费生成类型的测试(即 TypeScript 侧项目)
├── package.json           # 构建与类型生成脚本
├── turbo.json             # Turborepo 缓存配置
└── README.md

三个 Sway 程序都极其精简,正好覆盖 typegen 需要处理的三种程序形态:

合约demo-contract/src/main.sw)——暴露一个回显函数:

contract;

abi DemoContract {
    fn return_input(input: u64) -> u64;
}

impl DemoContract for Contract {
    fn return_input(input: u64) -> u64 {
        input
    }
}

脚本demo-script/src/main.sw)——main 直接返回常量:

script;

fn main() -> u8 {
    10
}

谓词demo-predicate/src/main.sw)——恒为真的签名校验逻辑:

predicate;

fn main() -> bool {
    true
}

每个 Sway 项目都有配套的 Forc.toml,声明项目名(如 name = "demo-contract")、入口文件 entry = "main.sw" 与许可证,fuels typegen 后续正是依据这些名字去定位编译产物中的 ABI 文件。

二、构建流程:编译 Sway 并生成类型

README 给出的核心命令是:

pnpm build

并注明“详见 package.json 中的 scripts”。下面完整列出 package.json 中的脚本及其执行顺序,这是理解 typegen 流水线的关键。

"scripts": {
    "pretest": "run-s build:forc build:types",
    "build:forc": "run-p forc:*",
    "forc:contract": "pnpm fuels-forc build -p demo-contract --release",
    "forc:script": "pnpm fuels-forc build -p demo-script --release",
    "forc:predicate": "pnpm fuels-forc build -p demo-predicate --release",
    "build:types": "run-p types:*",
    "types:contract": "pnpm fuels typegen -i demo-contract/out/release/demo-contract-abi.json -o src/contract-types",
    "types:script": "pnpm fuels typegen -i demo-script/out/release/demo-script-abi.json -o src/script-types --script",
    "types:predicate": "pnpm fuels typegen -i demo-predicate/out/release/demo-predicate-abi.json -o src/predicate-types --predicate"
}

流程分两个阶段串行执行(run-s 串行、run-p 并行):

2.1 阶段一:build:forc 编译 Sway 程序

build:forc 通过 run-p forc:* 并行触发三个 forc 编译任务,每个任务形如:

pnpm fuels-forc build -p demo-contract --release

参数说明:

  • -p demo-contract:指定要编译的 Sway 包(对应 Forc.toml 中的 name);
  • --release:以 release 模式编译,产物落在 <包目录>/out/release/ 下,包含字节码(.bin)、ABI(<包名>-abi.json)、存储槽位(storage_slots.json)等。

fuels-forc 来自 devDependencies 中的 @internal/forc(workspace 内部包,internal/forc),作用是安装/调用与当前 fuels-ts 版本匹配的 Forc 编译器,保证工具链版本一致。

2.2 阶段二:build:types 生成 TypeScript 类型

编译完成后,build:types 并行运行三个 typegen 任务,每个任务对应一种程序类型:

pnpm fuels typegen -i demo-contract/out/release/demo-contract-abi.json -o src/contract-types
pnpm fuels typegen -i demo-script/out/release/demo-script-abi.json -o src/script-types --script
pnpm fuels typegen -i demo-predicate/out/release/demo-predicate-abi.json -o src/predicate-types --predicate

参数含义:

参数 含义
-i <path> 输入 ABI 文件路径,即 forc 编译产物中的 <包名>-abi.json
-o <dir> 生成类型的输出目录
--script 声明输入为脚本程序,生成脚本调用客户端
--predicate 声明输入为谓词程序,生成谓词客户端
(无标志) 默认为合约类型,生成合约与合约工厂客户端

需要说明的是,README 中“类型生成在 src/generated-types 内”的描述对应早期单合约形态;从当前 package.json 的脚本看,实际输出已按程序类型分为三个目录:src/contract-typessrc/script-typessrc/predicate-types,与测试代码中的导入路径一致。

fuels typegen 命令的实现位于 packages/fuels/src/cli/commands/build/generateTypes.ts。从源码结构看,CLI 将 -i 指定的 ABI 路径交给 @fuel-ts/abi-typegen 包的 runTypegen 执行,按程序类型分发到不同的模板生成逻辑;若未显式指定路径,则通过 getABIPaths 从 Forc 项目目录中自动发现 *-abi.json。对于 script 与 predicate,它还会额外把 out 目录下所有 *-abi.json(loader ABI)一并纳入生成范围(见 generateTypes.ts),因为脚本与谓词除业务代码外还有独立的 loader 部分。类型模板与生成引擎主体在 packages/abi-typegen 包中。更完整的参数说明可参考官方文档 generating-types.mdusing-generated-types.md

三、使用生成的类型:从部署到调用

demo.test.ts 完整演示了三种生成类型的消费方式。它从 fuels 导入运行时能力,从生成目录导入强类型客户端:

import { toHex, Address, Wallet, FuelError, ErrorCode } from 'fuels';
import { expectToThrowFuelError, launchTestNode } from 'fuels/test-utils';

import storageSlots from '../demo-contract/out/release/demo-contract-storage_slots.json';

import { DemoContract, DemoContractFactory } from './contract-types';
import { DemoPredicate } from './predicate-types';
import type { DemoPredicateInputs } from './predicate-types/DemoPredicate';
import { DemoScript } from './script-types';

注意 storageSlots 直接来自 forc 编译产物(demo-contract/out/release/demo-contract-storage_slots.json),这解释了为什么类型生成与 forc 编译必须按 pretest 脚本中的顺序执行。

3.1 合约:工厂部署与实例调用

生成的合约类型包含两个关键导出:DemoContractFactory(负责部署)与 DemoContract(已部署合约实例)。测试中展示了三种等价的部署/调用路径:

方式一:带存储槽位部署demo.test.ts):

const { waitForResult } = await DemoContractFactory.deploy(wallet, {
  storageSlots,
});
const { contract } = await waitForResult();
expect(contract.id).toBeTruthy();

方式二:实例化工厂后部署,再调用函数

const factory = new DemoContractFactory(wallet);
const deploy = await factory.deploy();
const { contract } = await deploy.waitForResult();
const contractId = contract.id;

const { waitForResult } = await contract.functions.return_input(1337).call();
const { value } = await waitForResult();
expect(value.toHex()).toEqual(toHex(1337));

方式三:跳过部署,用已知合约 ID 直接连接实例demo.test.ts):

const contractInstance = new DemoContract(contractId, wallet);
const call2 = await contractInstance.functions.return_input(1337).call();
const { value: v2 } = await call2.waitForResult();

可以看到,return_input(u64) -> u64 的 Sway 签名被 typegen 映射为强类型的 contract.functions.return_input(1337) 调用链:functions 下按函数名组织、.call() 发起交易、waitForResult() 等待回执并解包返回值。类型层面的函数名、参数个数与返回类型都由 ABI 推导,调用侧无需手写 JSON 编码。

测试还验证了生成类型的错误边界:用无资金的钱包 simulate() 会抛出 INSUFFICIENT_FUNDS_OR_MAX_COINSFuelError,而 dryRun() 不消耗资金、不会抛错(demo.test.ts),说明生成的合约客户端与 fuels 的 Provider/资金检查体系是打通的。

3.2 脚本:以类钱包方式执行 main

脚本类型的用法与合约显著不同——没有部署概念,直接用钱包构造实例并调用 maindemo.test.ts):

const script = new DemoScript(wallet);
const { waitForResult } = await script.functions.main().call();
const { value } = await waitForResult();
expect(value).toStrictEqual(10);

返回值 10 与 Sway 侧 fn main() -> u8 { 10 } 严格对应。

3.3 谓词:构造、注资与转出

谓词类型的使用分三步(demo.test.ts):

const receiver = Wallet.fromAddress(Address.fromRandom(), provider);

// 1. 按 ABI 定义构造谓词输入数据
const predicateData: DemoPredicateInputs = [];
const predicate = new DemoPredicate({ provider, data: predicateData });

// 2. 向谓词地址转入资金
const tx = await wallet.transfer(predicate.address, 200_000, await provider.getBaseAssetId());
const { isStatusSuccess } = await tx.wait();

// 3. 由谓词转出资金给接收方,验证签名成功
const tx2 = await predicate.transfer(receiver.address, 50_000, await provider.getBaseAssetId());
await tx2.wait();

expect((await receiver.getBalance()).toNumber()).toEqual(50_000);

这里 DemoPredicateInputs 是 typegen 依据 fn main() -> bool 签名推导出的输入数据类型;谓词客户端构造时需要 providerdata 两个字段,之后即可像普通账户一样发起 transfer。本示例谓词恒返回 true,因此转出必然成功;真实场景中 data 会承载签名、白名单地址等校验参数。

四、缓存与工程化要点

  • turbo.json 声明了 pretest 任务的 inputs(Sway 源码)与 outputsout/release/** 编译产物),Turborepo 可据此对编译与生成结果做增量缓存;
  • 依赖上该包仅依赖 workspace 内的 fuels(运行时)与 @internal/forc(编译器工具链),见 package.json,说明整条 typegen 流水线都可用 fuels-ts 自带的 CLI 完成;
  • 运行测试即触发完整流程:pnpm pretestbuild:forcbuild:types,随后 vitest 执行 demo.test.ts 中的用例。

五、小结

apps/demo-typegen 为骨架,fuels-ts 的类型生成工作流可以概括为三步:

  1. fuels-forc build -p <包名> --release 编译 Sway 程序,产出 *-abi.json 等文件;
  2. fuels typegen -i <abi.json> -o <输出目录> 生成类型,按程序类型追加 --script--predicate 标志;
  3. 在 TypeScript 侧导入生成的工厂/实例类,以 functions.<方法名>(...).call() 链完成部署、调用与转账。

这套机制把 Sway ABI 的编码细节封装在生成代码里,开发者获得的是带完整类型提示的调用 API,这也是 fuels-ts “先编译、后 typegen、再编码”标准开发流程的最小可运行范例。

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

项目优选

收起
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