![fuels-ts 中的 Sway 定长字符串:str[x] 的创建、合约调用与长度校验原理](http://pic.xiahunao.cn/yaotu/fuels-ts 中的 Sway 定长字符串:str[x] 的创建、合约调用与长度校验原理)
fuels-ts 中的 Sway 定长字符串str[x] 的创建、合约调用与长度校验原理【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts导读在 Sway 语言中字符串是**定长statically-sized**的开发者必须在使用前声明其大小类型写法为str[x]其中x表示字符串长度。本文以 fuels-tsFuel Network TypeScript SDK仓库中的官方类型指南为骨架讲解如何在 SDK 中创建与str[x]一一对应的 JavaScript 字符串、如何把它们传入合约方法并取回结果以及当长度不符时 SDK 底层编码器为何会抛出Value length mismatch during encode错误。读完本文你将能正确、安全地在 fuels-ts 项目中处理定长字符串参数并理解长度校验背后StringCoder的完整实现链路。什么是str[x]Sway 的定长字符串类型Sway 中的字符串不像大多数语言那样是“可变的、长度自增长的”对象。在 Sway 类型指南 中明确写到In Sway, strings are statically-sized, which means you must define the size of the string beforehand. Statically-sized strings are represented using thestr[x]syntax, wherexindicates the strings size.也就是说str[x]在 ABI 层是一个长度固定为x的字符串例如str[2]只能容纳长度为 2 的字符串str[8]只能容纳长度为 8 的字符串。在 SDK 一侧str[x]会被映射为原生 JavaScriptstring。因此在使用 fuels-ts 调用合约时你需要保证传给合约的字符串“尺寸”与 Sway 中声明的x严格一致。从 Sway 到 SDK一个真实的 ABI 示例为了观察定长字符串在真实合约方法中的形态可以查看 fuels-ts 文档仓库配套的 Sway 合约 apps/docs/sway/echo-values/src/main.sw。它定义了一个EchoValuesABI其中的字符串相关方法如下abi EchoValues { fn echo_str_8(value: str[8]) - str[8]; fn echo_str(value: str) - str; } impl EchoValues for Contract { fn echo_str_8(value: str[8]) - str[8] { value } fn echo_str(value: str) - str { value } }echo_str_8接收一个str[8]并原样返回。当通过fuels的 typegen 流程把该 ABI 生成 TypeScript 绑定后合约工厂如指南中使用的EchoValuesFactory会为这个函数生成对应的 SDK 方法。正如 字符串指南 所述When a contract method accepts and returns astr[8], the corresponding SDK wrapper method will also take and return a string of the same length.即Sway 方法签名中的str[8]参数与返回值会一一对应地表现为 SDK 方法中的“长度为 8 的字符串”参数与返回值类型系统上的桥接是透明、无额外包装的。在 SDK 中创建定长字符串创建一个定长字符串本质上就是创建普通 JavaScript 字符串但需要人工保证长度等于 Sway 侧声明的x。指南代码片段 apps/docs/src/guide/types/snippets/string.ts 给出了直观示例// Sway str[2] const stringSize2 st; // Sway str[8] const stringSize8 fuel-sdk;st与 Sway 的str[2]对应fuel-sdk正好 8 个字符与 Sway 的str[8]对应。在完整的使用流程中需要先建立 Provider、加载钱包并部署合约代码片段前置环境如下来自同一文件的full区域import { Provider, Wallet } from fuels; import { LOCAL_NETWORK_URL, WALLET_PVT_KEY } from ../../../env; import { EchoValuesFactory } from ../../../typegend; const provider new Provider(LOCAL_NETWORK_URL); const wallet Wallet.fromPrivateKey(WALLET_PVT_KEY, provider); const deploy await EchoValuesFactory.deploy(wallet); const { contract } await deploy.waitForResult();其中LOCAL_NETWORK_URL、WALLET_PVT_KEY等环境信息来自 apps/docs/src/env.tsEchoValuesFactory则由 apps/docs/sway/echo-values 目录下的合约经 typegen 生成。在合约方法中传入并接收str[8]准备好合约实例后可以直接调用echo_str_8方法并读取返回值const { value } await contract.functions.echo_str_8(fuel-sdk).get(); console.log(value, value); // fuel-sdk这里可以观察到两个关键点入参SDK 方法echo_str_8(fuel-sdk)直接接受一个普通 JS 字符串fuel-sdk无需任何额外包装返回值get()返回的value会被 SDK 解码回等长的 JS 字符串fuel-sdk与 Sway 侧“原样回传”的行为一致。整段示例位于 apps/docs/src/guide/types/snippets/string.ts。长度校验的底层实现StringCoder 源码解读“长度必须精确匹配”并不是 SDK 的软性约定而是由 ABI 编码器强制执行。fuels-ts 在 ABI 解析阶段会把str[x]解析为对应的StringCoder见 getCoderV1.tsconst stringMatch stringRegEx.exec(resolvedAbiType.type)?.groups; if (stringMatch) { const length parseInt(stringMatch.length, 10); return new StringCoder(length); }StringCoder的完整实现在 packages/abi-coder/src/encoding/coders/StringCoder.ts其核心逻辑如下export class StringCoderTLength extends number number extends Coderstring, string { constructor(length: TLength) { super(string, str[${length}], length); } encode(value: string): Uint8Array { if (value.length ! this.encodedLength) { throw new FuelError(ErrorCode.ENCODE_ERROR, Value length mismatch during encode.); } return toUtf8Bytes(value); } decode(data: Uint8Array, offset: number): [string, number] { // ... 长度校验与 toUtf8String 解码 return [toUtf8String(bytes), offset this.encodedLength]; } }从源码可以归纳出 SDK 底层的三件事实编码阶段构造器把 Sway 侧声明的长度存入encodedLength即str[8]→8。encode先执行value.length ! this.encodedLength检查不相等就抛出ErrorCode.ENCODE_ERROR错误信息为Value length mismatch during encode.相等时再通过toUtf8Bytes将字符串转成 UTF-8 字节序列。也就是说长度校验发生在编码前属于“防御性提前失败”避免把错误数据发上链。解码阶段decode按固定长度切片并校验字节数是否足够不满足时抛出ErrorCode.DECODE_ERROR错误信息为Invalid string data size./Invalid string byte data size.随后用toUtf8String还原为 JS 字符串并返回推进后的偏移量。长度语义这里的检查基于 JavaScript 字符串的.lengthUTF-16 码元数量而 Sway 的str[x]与编码后的字节数直接相关。从 StringCoder.ts 的实现看SDK 目前按“字符数等于编码长度”进行比对因此对纯 ASCII 定长字符串是完全一一对应的若涉及多字节 Unicode 字符实际编码后的 UTF-8 字节数可能大于 JS 侧的.length这正是“定长字符串要谨慎确认长度口径”的原因。测试用例佐证编码器的行为在 packages/abi-coder/src/encoding/coders/StringCoder.test.ts 中被完整覆盖例如it(should encode a string, () { const coder new StringCoder(4); const input fuel; const expected new Uint8Array([102, 117, 101, 108]); // f,u,e,l expect(coder.encode(input)).toStrictEqual(expected); }); it(throws when encoding a string that does not match coder size, async () { const coder new StringCoder(2); await expectToThrowFuelError( () coder.encode(fuel), new FuelError(ErrorCode.ENCODE_ERROR, Value length mismatch during encode.) ); });可以看到fuel与StringCoder(4)匹配时正常编码为 4 个字节当把fuel交给StringCoder(2)编码时则稳定抛出ENCODE_ERROR。这与合约调用场景中的报错是同一套机制。长度不匹配时会发生什么指南明确指出传入过长或过短的字符串都会导致调用失败If you pass a string that is either too long or too short for a contract method, the call will fail.配套示例位于 apps/docs/src/guide/types/snippets/string.tsconst longString fuel-sdk-WILL-THROW-ERROR; try { await contract.functions.echo_str_8(longString).call(); } catch (error) { console.log(error, error); // Value length mismatch during encode } const shortString THROWS; try { await contract.functions.echo_str_8(shortString).call(); } catch (error) { console.log(error, error); // Value length mismatch during encode }无论是 22 个字符的fuel-sdk-WILL-THROW-ERROR过长还是 6 个字符的THROWS过短只要不等于 8调用echo_str_8都会失败并抛出Value length mismatch during encode.错误——因为它根本无法通过上节所述StringCoder.encode的长度关卡错误发生在请求真正进入链上执行之前。在更贴近真实使用路径的集成测试中也能看到同一错误例如 packages/fuel-gauge/src/abi/abi-coder.test.ts 中即断言了该方法返回FuelError.CODES.ENCODE_ERROR与Value length mismatch during encode.。这进一步印证了“定长字符串参数长度必须精确一致”是 SDK 端强制的约束而不是运行时才暴露的链上行为。定长字符串的边界与注意事项结合指南与源码使用str[x]时需要记住以下边界长度必须严格相等过长或过短都会在编码阶段触发ENCODE_ERROR因此建议在业务代码中对入参长度做前置校验或从业务数据源头保证长度语义如固定长度的哈希、编码名、地址别名等。定长字符串不是唯一的字符串类型Sway 还存在动态长度的标准库字符串StdStringStandard Lib String其在 SDK 中同样映射为 JS 字符串但编码时会在头部附加u64长度字段、无需固定尺寸适合长度不固定的文本场景可参考 StdString 指南 与 StdStringCoder 实现 做对比。二者在 ABI 中形态不同需要根据实际业务挑选合适的类型。长度口径StringCoder用 JS 字符串的.length与 Sway 声明的长度比对。对于纯 ASCII 内容一个字符 一个字节两者完全等价涉及多字节 Unicode 时建议显式确认所期望的“长度”单位避免编码行为与直觉不符。错误属于编码期错误长度不匹配的报错是本地 ABI 编码阶段的ENCODE_ERROR错误码见 packages/errors/src/error-codes.ts可以通过捕获FuelError统一处理不会产生链上交易费用损耗。小结Sway 的str[x]是一种定长字符串类型fuels-ts 将其直接桥接为“长度必须精确匹配”的 JavaScript 字符串创建时按声明的x构造 JS 字符串调用合约时原生传入/接收即可而长度不符会在编码阶段被 StringCoder 以Value length mismatch during encode.提前拦截。理解这一从类型语法、ABI 解析到编码校验的完整链路可以帮助你在 fuels-ts 项目中放心地设计、校验与传递定长字符串参数。延伸阅读类型指南总览 | StdString 动态字符串指南 | RawSlice 指南【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考