使用 fuels-ts 的 Vite 模板快速搭建 Fuel dApp:开发流程、项目结构与测试网部署指南
【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts
本指南围绕当前仓库 templates/vite 中的 Vite + React 模板展开。该模板由
create-fuels脚手架生成,预置了 Sway 智能合约工作区、类型安全的前端调用代码与一整套开发/测试/部署脚本,是快速上手 Fuel Network dApp 开发的参考起点。读完本文,你将掌握 Fuel 本地开发节点的启动与合约热重载机制、fuels CLI 底层的工作原理,以及将 dApp 切换部署到测试网的完整配置方法。
一、模板是什么:一份可运行可扩展的 Fuel dApp 骨架
仓库 templates/vite 中存放着一个用 Vite,是 Fuel Network 的 TypeScript SDK,包含 CLI、类型生成器与链上交互能力。
模板并不是一个"空壳",它自带了完整的开发闭环:
- Sway 智能合约工作区templates/vite/sway-programs:包含
contract(合约)、predicate(谓词)、script(脚本)三个目录,对应 Fuel 的三大程序类型; - 类型安全的前端templates/vite/src:通过
fuelsCLI 生成的 API 与 React 组件完成钱包连接、合约调用、谓词解锁与资产领取等演示功能; - 一键式脚本templates/vite/package.json:覆盖本地开发、构建、Sway 单测、集成测试与 UI 测试;
- 环境切换机制templates/vite/src/lib.tsx:通过环境变量在"本地开发网络"与"公共测试网"之间切换 provider 与合约 ID。
gitignore中默认忽略了生成的类型文件(src/sway-api/contracts、predicates、scripts与index.ts),即这些文件由fuels build在本地生成、通常不入库,这正是模板保持了"源码可复现"的关键设计。
二、启动开发环境:一条命令拉起本地 Fuel 节点与合约热重载
模板 templates/vite/README.md 给出的起步步骤非常精简,仅需两个终端窗口:
1. 启动 Fuel 开发服务器(含本地节点 + 合约热重载)
npm run fuels:dev该命令对应package.json中的"fuels:dev": "fuels dev",调用的是fuelsCLI 的 dev 子命令。它在后台会做三件事(详见 packages/fuels/src/cli/commands/dev/index.ts):
- 自动拉起本地 Fuel Core 节点:依据 templates/vite/fuels.config.ts 中的
autoStartFuelCore(默认启用)启动一个fuel-core子进程。节点默认监听127.0.0.1:4000,其启动细节实现在 packages/fuels/src/cli/commands/dev/autoStartFuelCore.ts——可以看到它读取fuelCorePort(缺省通过portfinder找可用端口)、以--db-type in-memory运行,即本地节点的数据保存在内存中,重启后状态归零; - 编译并部署 Sway 程序:调用
buildAndDeploy完成build(编译 + 生成 TS 类型)与deploy(部署合约、谓词、脚本并回写 ID),部署产物会落到src/sway-api/; - 监听文件变更触发热重载:CLI 使用
chokidar监听 Sway 工作区。当sway-programs/**/*.sw、Forc.toml等文件被修改时,会重新执行编译与部署,日志输出File changed: <path>;当fuels.config.ts本身变化时,则重启整个 dev 进程以应用新配置。
2. 启动前端(Vite 开发服务器)
npm run dev该命令对应"dev": "vite",把 React 前端跑在 Vite 开发服务器上。浏览器打开页面后即可连接钱包、浏览 wallet / contract / predicate / script / faucet 等演示视图(视图路由由 templates/vite/src/hooks/useRouter.ts 提供,通过 URL 查询参数?v=<view>记录当前视图)。
注意:README 中提到的"Next.js development server"系模板通用文案遗留,实际本模板为 Vite + React 项目,请以第二条命令
npm run dev为准。
三、模板目录结构逐层拆解
从源码结构看,模板可划分为以下四个层次:
templates/vite/ ├── fuels.config.ts # fuels CLI 配置:Sway 工作区、输出目录、端口 ├── package.json # fuels:dev / dev / build / test 等脚本 ├── fuel-toolchain.toml # 固定 forc / fuel-core 工具链版本 ├── sway-programs/ # Sway 工作区(contract / predicate / script) │ ├── Forc.toml # workspace 声明 │ ├── contract/src/main.sw # Counter 合约 │ ├── predicate/src/main.sw # 密码谓词 │ └── script/src/main.sw # 原样返回输入的脚本 ├── src/ │ ├── lib.tsx # 环境变量与 provider/contractId 的集中管理 │ ├── main.tsx # FuelProvider、连接器、React Query 装配入口 │ ├── App.tsx # 连接/网络校验与视图切换 │ ├── hooks/ # useRouter、useNotification、useBaseAssetId │ ├── components/ # Wallet/Contract/Predicate/Script/Faucet 等 │ └── sway-api/ # fuels 生成的类型与 contract-ids.json(git 忽略) └── test/ ├── integration/ # contract/predicate/script 集成测试(vitest) └── ui/ # Playwright UI 测试与脚本fuels.config.ts:CLI 的"指挥中心"
fuels.config.ts 内容如下:
import { createConfig } from 'fuels'; import dotenv from 'dotenv'; import { providerUrl } from './src/lib'; dotenv.config({ path: ['.env.local', '.env'] }); // 若节点运行在 4000 之外的端口,可在此设置 const fuelCorePort = +(process.env.VITE_FUEL_NODE_PORT as string) || 4000; export default createConfig({ workspace: './sway-programs', // Sway 工作区路径 output: './src/sway-api', // 生成类型的保存位置 fuelCorePort, providerUrl, forcPath: 'fuels-forc', // 由 internal/forc 提供的 forc 封装 fuelCorePath: 'fuels-core', // 由 internal/fuel-core 提供的节点封装 });workspace/output:fuels CLI 编译哪些 Sway 程序、把生成的 TS 类型写到哪。生成物正是 templates/vite/src/components/Contract.tsx 等组件里import { TestContract } from "../sway-api"的来源;fuelCorePort:本地节点端口,读取自VITE_FUEL_NODE_PORT环境变量,缺省4000;providerUrl:CLI 启动节点时对外暴露的 GraphQL 端点,同样复用前端的src/lib.tsx;forcPath/fuelCorePath:指向fuels-forc/fuels-core。这两个由仓库内部包 internal/forc 与 internal/fuel-core 提供的二进制封装会在依赖安装时下载对应用户态工具链。
环境切换中枢 src/lib.tsx
模板设计了一个巧妙的"单点控制"文件 src/lib.tsx:所有组件都从这里读取 URL 与 Chain ID,而不是各自硬编码。
export const environment = process.env.VITE_DAPP_ENVIRONMENT || 'local'; export const isLocal = environment === 'local'; export const isTestnet = environment === 'testnet'; export const localProviderUrl = `http://127.0.0.1:${process.env.VITE_FUEL_NODE_PORT || 4000}/v1/graphql`; export const localChainId = 0; export const testnetProviderUrl = "https://testnet.fuel.network/v1/graphql"; export const testnetChainId = 0; export const providerUrl = isLocal ? localProviderUrl : testnetProviderUrl; export const providerChainId = isLocal ? localChainId : testnetChainId;- 本地模式使用
http://127.0.0.1:<port>/v1/graphql(VITE_FUEL_NODE_PORT缺省4000),测试网模式使用https://testnet.fuel.network/v1/graphql; - 合约 ID 同样分环境:本地从
sway-api/contract-ids.json读取,测试网读取VITE_TESTNET_CONTRACT_ID环境变量; - 交易链接展示也会随环境切换(本地直接显示 txId,测试网跳转到区块浏览器)。
钱包连接与网络校验
前端装配入口 src/main.tsx 使用@fuels/connectors的defaultConnectors、@fuels/react的FuelProvider与 React Query:
const connectors = defaultConnectors({ devMode: true, fuelProvider: new Provider(providerUrl), chainId: providerChainId, }); const networks = [{ url: providerUrl, chainId: providerChainId }];App.tsx 通过useIsConnected()、useNetwork()校验钱包连接与网络是否匹配当前 provider:未连接时展示 Connect 按钮,连错网络时提示切换到providerUrl,连接正确后才渲染五大功能视图。
四、自带的三个 Sway 示例程序
Counter 合约
contract/src/main.sw 定义了一个带存储的计数器合约:
abi Counter { #[storage(read)] fn get_count() -> u64; #[storage(write, read)] fn increment_counter(amount: u64) -> u64; } storage { counter: u64 = 0, }increment_counter读取当前值、累加amount后写回并返回最新值;文件内还内置了should_get_count与should_increment_counter两个 Sway 单测。前端 components/Contract.tsx 使用fuels生成的TestContract类型安全地完成get_count().get()(只读调用)与increment_counter(1).call()(交易提交 +waitForResult)操作,并带有交易提交通知与本地水龙头(LocalFaucet)支持。
密码谓词 Predicate
predicate/src/main.sw 是最简洁的谓词示例:仅当传入的password == 1337时返回true,谓词"解锁",交易才被允许执行,否则回滚。它演示了谓词作为"可编程签名条件"的核心理念,并带正反两个测试用例。
回显脚本 Script
script/src/main.sw 只做一件事:原样返回传入的input,用于演示脚本(Script)的调用方式。
三个程序由 sway-programs/Forc.toml 组成一个 workspace:
[workspace] members = ["contract", "predicate", "script"]这与 fuels CLI 的部署实现一一对应:仓库中 deploy 命令分别处理deployContracts、deployPredicates、deployScripts,并把合约 ID 等写回contract-ids.json(模板中 sway-api/contract-ids.json 的"dummy-contract-id"仅为占位值,真实 ID 由本地部署生成,且该文件不在默认忽略列表中、由部署流程更新)。
五、测试:从 Sway 单测到 UI 端到端
package.json 提供分层测试脚本:
npm test # 依次运行全部测试 npm run test:forc # forc test --path ./sway-programs → Sway 语言级单测 npm run test:e2e # vitest → 集成测试 npm run test:ui # Playwright UI 测试- Sway 单测:对工作区每个程序运行
forc test(即.sw文件内#[test]标注的用例); - 集成测试:
test/integration/下的 contract.test.ts、predicate.test.ts、script.test.ts,由 vitest 驱动(vitest.config.mts 排除了 UI 测试目录); - UI 测试:test/ui/test-ui.sh 先执行
playwright install --with-deps安装浏览器依赖,再运行playwright test(playwright.config.ts),把整条 dApp 用户路径(连接、合约调用、谓词解锁、领取水龙头等)走一遍。
六、从本地切换到测试网
模板 templates/vite/README.md 明确指出:将 dApp 部署到测试网需要参考官方Deploying to Testnet指南。结合模板源码,切换的核心是环境变量与 fuels 命令:
- 构建合约并部署到测试网:
fuels build(构建)、fuels deploy(部署),把部署返回的合约 ID 记录下来; - 配置环境变量,让前端指向测试网:
VITE_DAPP_ENVIRONMENT=testnet:把 provider/chainId/contractId 切换到测试网分支(见 src/lib.tsx);VITE_TESTNET_CONTRACT_ID=<部署得到的合约 ID>:指定测试网合约地址;- 不设置时默认走本地分支(
local),因此本地开发无需额外配置。
- 常规前端构建/预览:
npm run build(内部先执行fuels build生成最新类型,再tsc -b && vite build),之后即可托管产物。
此外 vercel.json 提供了 Vercel 静态托管的预设配置,方便把前端部署为静态站点,测试网浏览器上的交易链接则由lib.tsx的renderTransactionId自动生成。
七、小结:从模板到可扩展 dApp
templates/vite用最小成本串起了 Fuel dApp 开发的完整链路:Sway 工作区编译 → 自动拉起本地节点 → 类型安全的代码生成 → 前端钱包连接与网络切换 → 多层测试。理解它之后,你可以:
- 修改
sway-programs/contract/src/main.sw扩展合约逻辑(例如给 Counter 增加decrement方法),fuels dev会自动重新编译部署,前端类型同步更新; - 参考 templates/vite/src/components 与 fuels 生成的
sway-api类型,快速写出新的交互组件; - 沿用
lib.tsx的环境变量模式接入自己的测试网/主网合约。
模板本身还对应一份 Vite 双端模板的姊妹篇 templates/nextjs(同为 create-fuels 引导生成、面向 Next.js),以及在apps/下可以互相印证的 create-fuels-counter-guide 演示项目,可作为对照学习的延伸资料。
【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考