fuels-ts 钱包实例化完全指南:从私钥、助记词到 Provider 连接
【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts
导读
Wallet是 Fuel 官方 TypeScript SDK(fuels-ts)中创建与管理钱包的核心入口类,位于@fuel-ts/account包内。本指南以 instantiating-wallets.md 为主体,系统讲解在 SDK 中实例化钱包的全部途径:随机生成新钱包、从私钥/助记词/种子/HD 派生密钥/加密 JSON 文件还原已解锁钱包、仅凭地址创建锁定钱包,以及如何为钱包挂载Provider以便发起链上交易。阅读完成后,你将能够在任何 fuels-ts 项目中熟练选用合适的实例化方式,并理解WalletLocked与WalletUnlocked两种状态背后的设计动机。
说明:fuels-ts 中所谓"钱包",本质是围绕一组 Fuel 密钥(公私钥对)与地址的封装对象,它与常见 EOA 钱包交互模型类似,但实现针对 Fuel 网络地址格式做了专门适配。
先理解两个核心概念:WalletLocked与WalletUnlocked
在进入具体代码之前,必须先厘清 fuels-ts 的钱包对象模型,因为文档中所有实例化方式都围绕这两个类展开:
WalletUnlocked(已解锁钱包):持有私钥,可直接用于签名交易与消息。凡是从私钥、助记词、种子、HD 扩展密钥、加密 JSON 还原出来的钱包,默认都处于该状态。WalletLocked(锁定钱包):仅持有地址,不暴露私钥,只能用于只读查询(如查询余额、构建不含签名的交易),无法直接签名。一般通过Wallet.fromAddress(address)创建。
Wallet门面类把两者的静态工厂方法统一收口在一起,方便开发者只从fuels包导入一个类即可完成所有实例化。其源码可在 packages/account/src/wallet/wallet.ts 中查看,例如fromAddress直接返回new WalletLocked(address, provider),fromPrivateKey返回new WalletUnlocked(privateKey, provider),而generate、fromSeed、fromMnemonic、fromExtendedKey、fromEncryptedJson则全部委托给WalletUnlocked的同名静态方法(见 packages/account/src/wallet/wallets.ts)。
生成新钱包
当应用需要为新用户创建一套全新的、随机的密钥时,直接调用Wallet.generate()即可。该方法在底层生成一个随机密钥对,并返回一个立即可用的WalletUnlocked实例——无需任何额外的解锁或导入步骤。
// #region instantiating-wallets-1 import { Wallet } from 'fuels'; import type { WalletUnlocked } from 'fuels'; const wallet: WalletUnlocked = Wallet.generate(); // #endregion instantiating-wallets-1从源码看,Wallet.generate就是WalletUnlocked.generate的静态别名(见 packages/account/src/wallet/wallet.ts#L40),其实际实现在 packages/account/src/wallet/wallets.ts#L59,并且接受可选的GenerateOptions参数。
需要特别提醒的是:generate()产生的是全新随机密钥,SDK 不会替你保存它。上线前必须把私钥或助记词妥善备份(例如转为加密 JSON 存档),否则一旦丢失就无法再恢复资产。与之配套的备份方案(如WalletUnlocked.encrypt)可以参阅同一目录下的 encrypting-and-decrypting.md。
实例化已解锁钱包(WalletUnlocked)
还原已有钱包的核心思路是"用密钥材料重建WalletUnlocked"。fuels-ts 一共提供了五种互不相同的密钥材料入口,适用场景各不相同,下面逐一展开。
方式一:通过私钥导入
当你手上直接持有私钥(如从自己的数据库或环境变量读取)时,用Wallet.fromPrivateKey(privateKey)。私钥是燃料量形式的十六进制字符串,也可作为BytesLike传入(见 wallet.ts#L30)。
// #region instantiating-wallets-2 import type { WalletUnlocked } from 'fuels'; import { Wallet } from 'fuels'; const privateKey = '0x36ca81ba70f3e04b7cc8780bff42d907ebca508097d4ae3df5147c93fd217f7c'; const wallet: WalletUnlocked = Wallet.fromPrivateKey(privateKey); // #endregion instantiating-wallets-2⚠️ 安全提示:私钥即资产所有权。切勿将其硬编码在源码、提交进版本库或写入前端可见位置;生产环境应放入受保护的环境变量或密钥管理服务。
方式二:通过助记词导入
用户用钱包应用导出的一串 12/24 个英文单词即为助记词(BIP-39 mnemonic)。Wallet.fromMnemonic(mnemonic)会按默认派生路径从助记词推导出私钥并创建已解锁钱包。
// #region instantiating-wallets-3 import type { WalletUnlocked } from 'fuels'; import { Wallet } from 'fuels'; const mnemonic = 'section gospel lady april mouse huge prosper boy urge fox tackle orient'; const wallet: WalletUnlocked = Wallet.fromMnemonic(mnemonic); // #endregion instantiating-wallets-3对应方法签名在 wallets.ts#L89:fromMnemonic(mnemonic, provider?, path?, passphrase?)。其中path用于自定义 HD 派生路径,passphrase用于传入额外的 BIP-39 口令。fuels-ts 使用的默认派生路径与 Fuel 生态约定保持一致,通常无需改动。
方式三:通过种子导入
某些场景(如从硬件钱包或自建密钥库)直接拿到的是熵种子(seed)。此时用Wallet.fromSeed(seed):
// #region instantiating-wallets-4 import type { WalletUnlocked } from 'fuels'; import { Wallet } from 'fuels'; const seed = '0xa5d42fd0cf8825fc846b2f257887a515573ee5b779e99f060dc945b3d5504bca'; const wallet: WalletUnlocked = Wallet.fromSeed(seed); // #endregion instantiating-wallets-4方式四:通过 HD 派生扩展密钥导入
WalletUnlocked还支持直接传入由 BIP-32 风格推导出的扩展密钥(extended key)。典型组合是先用HDWallet.fromSeed(seed).toExtendedKey()得到扩展密钥,再交给Wallet.fromExtendedKey实例化:
// #region instantiating-wallets-5 import type { WalletUnlocked } from 'fuels'; import { HDWallet, Wallet } from 'fuels'; const seed = '0xa5d42fd0cf8825fc846b2f257887a515573ee5b779e99f060dc945b3d5504bca'; const extendedKey = HDWallet.fromSeed(seed).toExtendedKey(); const wallet: WalletUnlocked = Wallet.fromExtendedKey(extendedKey); // #endregion instantiating-wallets-5HDWallet同样由fuels统一导出,它实现了 Fuel 网络的层级确定性密钥派生逻辑。若你的应用需要实现 HD 钱包多账户体系(一个种子派生多个子私钥),建议深入阅读 mnemonic-wallet.md 与 packages/account/src/hdwallet 目录。
方式五:通过加密 JSON(keystore)导入
当钱包此前以加密 JSON 文件(keystore,遵循 Web3 Secret Storage 格式,默认使用 scrypt KDF + AES-128-CTR 加密)的形式备份时,需要密码配合解密才能还原,因此该方法是异步的,使用await:
// #region instantiating-wallets-6 import type { WalletUnlocked } from 'fuels'; import { Wallet } from 'fuels'; const jsonWallet = `{"id":"83d1792f-3230-496a-92af-3b44a1524fd6","version":3,"address":"ada436e1b80f855f94d678771c384504e46335f571aa244f11b5a70fe3e61644","crypto":{"cipher":"aes-128-ctr","mac":"6911499ec31a6a6d240220971730374396efd666bd34123d4e3ce85e4cf248c6","cipherparams":{"iv":"40576cbd4f7c84e88b0532320e23b425"},"ciphertext":"3e5e77f23444aa86b397dbc62e14d8b7d3fd7c7fe209e066bb7df17eca398129","kdf":"scrypt","kdfparams":{"dklen":32,"n":8192,"p":1,"r":8,"salt":"b046520d85090ee2abd6285174f37bc01e28846b6bb5edc03ae5f7c13aec03d2"}}}`; const password = 'password'; const wallet: WalletUnlocked = await Wallet.fromEncryptedJson( jsonWallet, password ); // #endregion instantiating-wallets-6从上面 JSON 可以看到 keystore 的关键字段:crypto.cipher(加密算法)、crypto.kdf与kdfparams(密钥派生函数及其参数)、crypto.ciphertext(密文)以及crypto.mac(完整性校验)。fuels-ts 对这类文件加密/解密的完整流程(含生产环境生成方式)见 encrypting-and-decrypting.md。
方式六:从锁定钱包解锁
还有一种常见形态:你手里只有一个仅含地址的WalletLocked,但随后又拿到了对应私钥。此时无需重新fromPrivateKey,直接对锁定钱包调用.unlock(privateKey)即可原地升级为已解锁钱包:
// #region instantiating-wallets-7 import type { WalletLocked, WalletUnlocked } from 'fuels'; import { Wallet } from 'fuels'; const address = '0x4cb2b5d2bdbcc8dbdbf91cd00be3e2deedb0ea0f34c969c0ed741a1925111a87'; const privateKey = '0x9deba03f08676716e3a4247797672d8008a5198d183048be65415ef89447b890'; const lockedWallet: WalletLocked = Wallet.fromAddress(address); const wallet: WalletUnlocked = lockedWallet.unlock(privateKey); // #endregion instantiating-wallets-7unlock返回一个新的WalletUnlocked,其中的私钥可通过wallet.privateKey访问。反向操作(把已解锁钱包降级为仅地址的锁定钱包)以及加解密、锁定的生命周期管理,可参见 locking-and-unlocking.md。
实例化锁定钱包(WalletLocked)
只读场景(如监控某个地址的余额、读取该地址相关的链上状态)不需要私钥,仅用 Fuel 的 b256 地址即可创建WalletLocked。Wallet.fromAddress接受形如0x+ 64 位十六进制的B256Address字符串(也支持Address对象,见 wallet.ts#L19):
// #region instantiating-wallets-8 import type { B256Address, WalletLocked } from 'fuels'; import { Wallet } from 'fuels'; const address: B256Address = `0x6d309766c0f1c6f103d147b287fabecaedd31beb180d45cf1bf7d88397aecc6f`; const wallet: WalletLocked = Wallet.fromAddress(address); // #endregion instantiating-wallets-8在 fuels-ts 的默认导出之外,WalletLocked、WalletUnlocked与HDWallet等类型与类均会一并从fuels入口导出,因此import type { WalletLocked } from 'fuels'即可获得完整的类型提示。
连接 Provider:让钱包具备链上能力
钱包本身可以作为纯离线密钥容器独立存在,但任何需要与区块链交互的操作——查询余额、估算手续费、提交交易、读取合约——都必须让钱包关联一个Provider。Provider负责与 Fuel 节点的 GraphQL/HTTP 接口通信,是钱包访问链上世界的通道。
场景 A:为已有钱包补挂 Provider
先创建 Provider,再用wallet.connect(provider)动态绑定:
// #region instantiating-wallets-9 import type { WalletLocked } from 'fuels'; import { Provider, Wallet } from 'fuels'; import { LOCAL_NETWORK_URL, WALLET_ADDRESS } from '../../../../env'; const provider = new Provider(LOCAL_NETWORK_URL); const wallet: WalletLocked = Wallet.fromAddress(WALLET_ADDRESS); wallet.connect(provider); // #endregion instantiating-wallets-9connect会就地给该钱包实例关联 Provider,之后便可调用余额查询、交易构建等接口。示例中的LOCAL_NETWORK_URL、WALLET_ADDRESS来自文档环境的 env.ts,实际开发时通常指向本地fuel-core节点的 URL 与某个测试地址。
场景 B:实例化时直接注入 Provider
多数情况下更推荐这种写法——把provider作为第二个可选参数直接传给工厂方法,一步到位:
// #region instantiating-wallets-10 import type { WalletLocked } from 'fuels'; import { Provider, Wallet } from 'fuels'; import { LOCAL_NETWORK_URL, WALLET_ADDRESS } from '../../../../env'; const provider = new Provider(LOCAL_NETWORK_URL); const wallet: WalletLocked = Wallet.fromAddress(WALLET_ADDRESS, provider); // #endregion instantiating-wallets-10这一可选参数模式贯穿全部工厂方法:fromPrivateKey、fromMnemonic、fromSeed、fromExtendedKey等都将provider?作为可选参数(参见 wallet.ts#L19-L80),即在导入密钥的同时完成链连接。因此你完全可以把第 3~6 小节的代码改写为Wallet.fromMnemonic(mnemonic, provider)等形态,获得可直接交易的钱包。
关于 Provider 的创建细节(如链上配置读取、本地节点 URL、网络切换),可进一步查阅 provider/index.md。
如何选择正确的实例化方式
| 目标状态 | 现有材料 | 推荐 API | 备注 |
|---|---|---|---|
| 新账户 | 无 | Wallet.generate() | 生成随机密钥,需自行安全备份 |
| 已解锁 | 私钥 | Wallet.fromPrivateKey(pk) | 最常见的恢复方式 |
| 已解锁 | 助记词 | Wallet.fromMnemonic(mnemonic) | 支持自定义 path/passphrase |
| 已解锁 | 种子 | Wallet.fromSeed(seed) | 直接输入熵种子 |
| 已解锁 | HD 扩展密钥 | Wallet.fromExtendedKey(xkey) | 常配合HDWallet使用 |
| 已解锁 | 加密 JSON + 密码 | await Wallet.fromEncryptedJson(json, password) | 唯一异步路径 |
| 已解锁 | WalletLocked+ 私钥 | lockedWallet.unlock(pk) | 就地升级为已解锁钱包 |
| 锁定(只读) | b256 地址 | Wallet.fromAddress(addr) | 也可传入Address对象 |
| 任意钱包 | 已有 + Provider | wallet.connect(provider) | 实例化后补绑链连接 |
| 任意钱包 | 密钥材料 + Provider | Wallet.fromXxx(mat, provider) | 实例化时直接绑定 |
选择依据可归纳为三条:
- 需要签名吗?需要则用已解锁钱包(持有私钥);只读查询则用
fromAddress创建锁定钱包更安全。 - 手头有什么密钥材料?按"私钥 / 助记词 / 种子 / 扩展密钥 / 加密 JSON"五选一,对应五条
fromXxx路径。 - 要不要立即上链?需要则顺手传入
provider参数,或用connect补挂。
底层原理与验证
所有工厂方法都收敛于 packages/account/src/wallet/wallets.ts 中WalletUnlocked/WalletLocked的实现:
generate(wallets.ts#L59)内部生成随机密钥对,返回WalletUnlocked,即已解锁钱包。fromSeed/fromMnemonic/fromExtendedKey(wallets.ts#L73-L109)负责将不同格式的种子/短语/扩展密钥统一转换为私钥,再装配为WalletUnlocked。从源码结构可以推断,助记词路径内部必然经过 BIP-39 校验与 HD 派生两步,最后落到与fromSeed相同的私钥装配逻辑。Wallet门面类(wallet.ts)只是这些静态方法的薄封装,提供统一的导入入口。
仓库中也配套了针对钱包实例化行为的基础测试,如 wallet.test.ts 与 wallet-unlocked.test.ts,它们验证了生成、导入、解锁等关键路径的行为,可作为理解 API 契约的补充素材。
延伸阅读
钱包生命周期不止于"实例化"。在同一guide/wallets目录下,还有与本文直接相关的进阶主题:
- locking-and-unlocking.md:
WalletLocked/WalletUnlocked双向转换与加解锁机制; - encrypting-and-decrypting.md:keystore JSON 的加密/解密(配合
fromEncryptedJson使用); - mnemonic-wallet.md:HD 助记词钱包的派生与管理(配合
fromMnemonic/fromExtendedKey); - private-keys.md:私钥的安全存储与使用建议;
- checking-balances.md:实例化完成后如何查询余额等链上数据;
- signing.md:使用
WalletUnlocked签名交易与消息。
【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考