Fuel TypeScript SDK 谓词可配置常量(Predicate Configurable Constants)实战:用白名单地址实现可控的资产转账

发布时间:2026/9/10 13:48:47
Fuel TypeScript SDK 谓词可配置常量(Predicate Configurable Constants)实战:用白名单地址实现可控的资产转账 Fuel TypeScript SDK 谓词可配置常量Predicate Configurable Constants实战用白名单地址实现可控的资产转账【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts导读在 Fuel 网络上谓词Predicate是一段可编程的签名——只有满足其返回true的条件时锁定的资产才可被花费。本篇文章聚焦 fuels-ts SDK 中谓词的可配置常量Configurable Constants能力以一个仅允许向白名单地址转账的资产校验场景为线索完整讲解如何在 Sway 中声明configurable常量、如何在 TypeScript 中通过生成类型typegen的谓词类实例化并覆盖常量默认值以及修改常量实际会生成新谓词、新地址这一核心机制背后的字节码原理。读完你将能独立使用fuelsSDK 编写、部署并驱动带可配置常量的自定义谓词。为什么谓词需要可配置常量可配置常量是 Sway 语言提供的一种内置能力在编写合约contract、脚本script与谓词predicate时你可以声明一些带默认值的常量并在部署/花费前按需覆盖其中的部分或全部取值。仓库文档对同一能力在合约与脚本中的用法亦有专门介绍参见 contracts/configurable-constants.md 与 scripts/configurable-constants.md。对谓词而言可配置常量的价值尤为明显谓词本身是无状态的判定程序其逻辑写死在字节码中。如果白名单地址、费率、阈值这类参数需要随业务变化没有 configurable 时你就得为每种取值重写并重新编译一份 Sway 程序。而借助可配置常量同一份编译产物、同一套 TypeScript 生成类型就能在运行时派生出不同取值的谓词实例从而让一份谓词程序适配多种业务场景。在 fuels-ts 仓库的 fuel-gauge 包中就存在以PredicateWithConfigurable包含FEE、ADDRESS两个 configurable为对象的完整测试用于验证默认值与覆盖值两种路径下谓词的锁定与花费详见 predicate-configurables.test.ts这是下文示例的官方等价佐证。场景与 Sway 侧实现白名单地址谓词我们考虑一个典型场景某谓词用于校验资产转账只有当收款方地址在预先批准的白名单中时这笔转账才会被放行。下面这份 Sway 谓词实现了该逻辑仓库中的完整源码位于 whitelisted-address-predicate/src/main.swpredicate; configurable { WHITELISTED: b256 0xa703b26833939dabc41d3fcaefa00e62cee8e1ac46db37e0fa5d4c9fe30b4132, } fn main(address: b256) - bool { WHITELISTED address }逐个要点拆解这份代码第 46 行声明了一个可配置常量块configurable { ... }其中常量名为WHITELISTED类型是b25632 字节地址类型默认值为一段十六进制地址。常量块末尾的逗号不能省略这是 Swayconfigurable块的语法要求。main函数接收一个b256参数address即谓词被花费时由交易方传入的主参数对应 SDK 侧的data并把该参数与常量WHITELISTED做相等比较相等才返回true、放行花费。因此当前的白名单地址完全由WHITELISTED常量的取值决定默认情况下白名单地址就是第 5 行写死的那个值。对应的工程清单 Forc.toml 只声明了项目名whitelisted-address-predicate、入口main.sw等元信息[dependencies]为空即该谓词不依赖任何外部库非常适合作为最小可运行示例。提示谓词在 Sway 中经forc build编译后会得到**字节码binary**与JSON ABI两份产物。关于谓词的基本实例化方式bytes、chainId、address三个关键属性以及多参数、结构体参数的传入可参考同目录下的 instantiating-a-predicate.md。本场景中的WhitelistedAddressPredicate即是由 fuels 提供的 typegen类型生成流程基于 ABI 自动生成出的类其构造参数data对应main的入参addressconfigurableConstants则对应configurable块。SDK 侧实例化一覆盖 WHITELISTED 常量当业务上需要把另一个地址加入白名单时我们需要用新地址覆盖WHITELISTED常量。fuels SDK 的谓词类构造函数原生支持configurableConstants选项调用方式见仓库示例 configurable-set-data.tsimport { Wallet, Provider } from fuels; import { LOCAL_NETWORK_URL, WALLET_ADDRESS, WALLET_PVT_KEY_2, } from ../../../../env; import { WhitelistedAddressPredicate } from ../../../../typegend/predicates/WhitelistedAddressPredicate; const provider new Provider(LOCAL_NETWORK_URL); const baseAssetId await provider.getBaseAssetId(); const whitelisted Wallet.fromAddress(WALLET_ADDRESS, provider); const sender Wallet.fromPrivateKey(WALLET_PVT_KEY_2, provider); const recipient Wallet.generate({ provider }); const configurable { WHITELISTED: whitelisted.address.toB256() }; // Instantiate predicate with configurable constants const predicate new WhitelistedAddressPredicate({ provider, data: [configurable.WHITELISTED], configurableConstants: configurable, }); // Transferring funds to the predicate const tx1 await sender.transfer(predicate.address, 200_000, baseAssetId, { gasLimit: 1000, }); await tx1.waitForResult(); const amountToTransfer 100; // Transferring funds from the predicate to destination if predicate returns true const tx2 await predicate.transfer( recipient.address, amountToTransfer, baseAssetId, { gasLimit: 1000, } ); await tx2.waitForResult();这段代码的关键点configurableConstants与data必须保持一致构造时configurableConstants把WHITELISTED覆盖为whitelisted.address.toB256()同时data传入同一个地址值作为main(address)的实参。这样当谓词校验WHITELISTED address时两侧相等返回true花费才被放行。这是本示例中交易能成功的关键前提。先注资、再花费的流程tx1由普通钱包sender向predicate.address转账200_000把资产锁定进谓词地址随后tx2由谓词发起transfer把其中100转给任意recipient——谓词在链下先行用data求值返回true即被当作合法签名参与交易签名。gasLimit等交易参数照常指定谓词发起的转账与普通Wallet转账共用同一套BaseTransactionRequest参数体系示例中gasLimit: 1000为演示值实际应以链上测算为准。官方测试 predicate-configurables.test.ts 复现了同样的覆盖路径先断言configurableConstants.FEE不等于默认值再以{ provider, data: [...], configurableConstants }实例化并完成花费随后校验目标地址余额精确等于转账额。SDK 侧实例化二沿用 Sway 默认白名单地址如果待校验的收款方恰好就是 Sway 源码中写死的默认白名单地址那么不必再传入configurableConstants——此时谓词将直接采用configurable块中的默认值参与求值。对应示例见 configurable-default.tsimport { Wallet, Provider } from fuels; import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from ../../../../env; import { WhitelistedAddressPredicate } from ../../../../typegend/predicates/WhitelistedAddressPredicate; const provider new Provider(LOCAL_NETWORK_URL); const baseAssetId await provider.getBaseAssetId(); const sender Wallet.fromPrivateKey(WALLET_PVT_KEY, provider); const recipient Wallet.generate({ provider }); // Instantiate predicate without configurable constants (will use the address defined in Sway) const predicate new WhitelistedAddressPredicate({ provider, data: [0xa703b26833939dabc41d3fcaefa00e62cee8e1ac46db37e0fa5d4c9fe30b4132], }); // Transferring funds to the predicate const tx1 await sender.transfer(predicate.address, 200_000, baseAssetId, { gasLimit: 1000, }); await tx1.waitForResult(); const amountToTransfer 100; // Transferring funds from the predicate to destination if predicate returns true const tx2 await predicate.transfer( recipient.address, amountToTransfer, baseAssetId, { gasLimit: 1000, } ); await tx2.waitForResult();此时构造器只提供provider与data其中data直接填入了 Sway 源码中WHITELISTED的默认值0xa703b26833939dabc41d3fcaefa00e62cee8e1ac46db37e0fa5d4c9fe30b4132。由于谓词将以该默认常量求值且与data相等校验照样通过。值得留意的是即便两个实例都能成功放行转账它们的谓词地址是不同的。使用默认常量的实例与覆盖常量的实例是两份不同的字节码派生出的不同地址这正是下一节要展开的核心机制。在 predicate-configurables.test.ts 中作者用不传configurableConstants、仅让data与默认值一致的方式验证了默认值路径同样成立。核心机制为什么覆盖常量会得到一个新谓词文档中反复强调这些定制化并不会直接修改原有谓词。原因在于谓词的地址是其字节码的哈希。任何对字节码的改动包括修改某个常量值都会产生不同的字节码进而得到不同的哈希最终派生出一个带新地址的新谓词。这与比特币的 P2SHPay-to-Script-Hash思想一脉相承判定能否花费的依据始终是调用脚本的哈希是否等于输入中锁定的哈希。因此谓词地址本质上是承诺commitment了完整字节码内容常量取值是字节码不可分割的一部分。将这一机制落实到 fuels SDK 的源码可从 predicate.ts 的实现中印证构造参数类型PredicateParamsTData, TConfigurables中定义了可选字段configurableConstants?: TConfigurables见 predicate.ts#L50构造器会把其存入实例的configurableConstants属性见 predicate.ts#L79-L92。SDK 对外暴露了setConfigurableConstants(configurableConstants)这样的实例方法见 predicate.ts#L203-L224只有当传入的常量对象非空时才进入Predicate.setConfigurableConstants(...)内部逻辑把常量重新编码进一份新的谓词字节码。底层静态方法setConfigurableConstants见 predicate.ts#L276-L300会遍历Object.entries(configurableConstants)结合 ABI 中记录的 configurable 偏移量把新值写入字节码的对应槽位基于这份新字节码重新计算出的哈希就是新谓词的地址。也就是说从 SDK 行为到链上语义是一致的我们并没有改变原谓词的行为而是创建了一个具有不同配置的新谓词。在使用层面这意味着两点实操提醒资金必须先转入新配置派生的那个谓词地址如第一个示例中sender.transfer(predicate.address, ...)旧地址下的资金不受新配置影响若同一个配置需要复用于多个地址应缓存并复用同一个Predicate实例避免因配置差异产生地址对不上的问题。类型安全与工程化要点在上述示例中WhitelistedAddressPredicate来自../../../../typegend/predicates/WhitelistedAddressPredicate这样的生成代码目录即 fuels typegen 流程根据谓词的 ABI 自动生成的类型化封装。基于这一封装configurableConstants的对象键如WHITELISTED与 Swayconfigurable块保持一一对应类型由生成器推导TConfigurables写错常量名或类型会在编译期报错data数组的形态由main的参数列表决定本场景是单个b256即以数组形式传入一个字符串fuels-ts 仓库的各应用与文档站均通过fuels.config.ts配置 typegen 输出路径例如 apps/docs/fuels.config.tsforc build与类型生成通常由fuelsCLI 统一驱动。因此真实的落地路径可以归纳为三步在 Sway 中编写带configurable块的谓词并forc build编译通过 fuels 的 typegen 生成对应的类型化谓词类在应用代码里以dataconfigurableConstants组合实例化谓词必要时用setConfigurableConstants在后续再次派生新配置的谓词。小结可配置常量为谓词提供了一种一份程序、多变配置的灵活机制Sway 侧用configurable { NAME: TYPE default }声明main中直接按常量名引用SDK 侧typegen 生成的谓词类通过configurableConstants构造参数覆盖默认值data同步传入匹配的主参数即可驱动校验语义侧谓词地址是字节码哈希覆盖常量必然生成新字节码与新地址因此你得到的是不同配置的新谓词而非被改写行为的旧谓词。以本场景为例无论白名单地址是沿用 Sway 默认值还是运行时被覆盖为新地址只要保证传入谓词的address参数与该配置下的WHITELISTED相等资产转账校验就会放行而不同白名单配置天然对应不同地址资金池彼此隔离——这正是可配置常量增强谓词健壮性、可定制性与安全边界的价值所在。仓库中 fuel-gauge 的配置化谓词测试 与 谓词 SDK 实现 可作为继续深入研究的入口。【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考