十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

fuels-ts 如何手动部署 SRC14 代理合约并升级合约目标

fuels-ts 如何手动部署 SRC14 代理合约并升级合约目标 fuels-ts 如何手动部署 SRC14 代理合约并升级合约目标【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts在 fuels-tsFuel Network TypeScript SDK中如果你希望合约可以后续升级、且对外暴露的地址保持不变需要把合约部署在一个代理合约后面先部署真实合约再部署一个 SRC14 兼容的 owned 代理合约并把目标指向该合约之后所有调用都通过代理合约 ID 进行升级时只需部署新版合约并更新代理的目标。fuels包导出了Src14OwnedProxy和Src14OwnedProxyFactory就是官方推荐使用的 SRC14 合规 owned 代理合约的 TypeScript 实现与fuels deploy命令内部使用的代理一致。官方文档也提示自动化场景下更推荐直接用fuels deploy命令它会替你完成部署与升级的全部流程下面的手动流程适合你想自己控制每一步、或需要定制部署逻辑的情况。完整来源见 Proxy Contracts 指南 和 Deploying Contracts 指南。准备条件项目中安装fuels包版本以 版本页 为准安装命令为pnpm add fuelsfuels版本号。编写 Sway 合约并构建产物运行forc build或推荐通过 Fuels CLI 运行fuels buildfuels build提供端到端的类型支持。用 Typegen 为合约生成 TypeScript 类型pnpm fuels typegen -i ./abis/*-abi.json -o ./types-i后面是合约构建后生成的、以-abi.json结尾的 ABI 文件路径-o是生成类型的输出目录合约类型默认就会生成--contract是默认值可省略。需要一个可连接的 Fuel 节点地址和一个部署者钱包的私钥用于创建Provider和Wallet。文档示例使用一个计数器合约演示整个流程。初始版本v1只有一个counter存储槽contract; abi Counter { #[storage(read)] fn get_count() - u64; #[storage(write, read)] fn increment_count(amount: u64) - u64; #[storage(write, read)] fn decrement_count(amount: u64) - u64; } storage { counter: u64 0, }后续升级版本v2新增了increments存储槽和get_increments()方法用来模拟“新版本合约多了一个存储槽”的升级场景。两个完整 Sway 文件分别在 counter 和 counter-v2两者都要构建并各自生成类型示例中的Counter、CounterFactory、CounterV2、CounterV2Factory就来自这一步的产物。第一步部署被代理的真实合约使用 Typegen 生成的CounterFactory部署合约import { Provider, Wallet } from fuels; import { CounterFactory } from ./types; // fuels typegen 的输出目录 const provider new Provider(本地节点URL); const wallet Wallet.fromPrivateKey(部署者钱包私钥, provider); const counterContractFactory new CounterFactory(wallet); const deploy await counterContractFactory.deploy(); const { contract: counterContract } await deploy.waitForResult();上面两处本地节点URL和部署者钱包私钥需要替换为你自己的值。deploy方法会按合约大小自动选择部署方式小合约用单个 create 交易大合约自动拆成 blob 分块提交交易后即返回contractId、waitForTransactionId和waitForResultwaitForResult()解析后才会拿到contract实例。注意如果用 blob 方式部署大合约需要多个交易且每个交易都要等待出块部署耗时会明显变长。第二步部署 SRC14 代理合约并初始化目标部署代理时有两个必须传的参数storageSlots要把真实合约的存储槽和Src14OwnedProxy自身的存储槽合并后传给代理以便在部署时初始化全部存储槽configurableConstants这是该 SRC14 合约特有的可配置常量部署时必须传入之后再调用initialize_proxy完成代理初始化。INITIAL_TARGET指向第一步部署的合约 IDINITIAL_OWNER是代理的所有者地址文档用的是部署者钱包地址。import { Src14OwnedProxy, Src14OwnedProxyFactory } from fuels; /** * 必须把全部存储槽传给代理以完成存储槽初始化 */ const storageSlots counterContractFactory.storageSlots.concat( Src14OwnedProxy.storageSlots ); /** * 这些可配置常量是文档推荐的 SRC14 合规合约特有的 * 部署时必须传入之后还要调用 initialize_proxy 完成代理初始化 */ const configurableConstants { INITIAL_TARGET: { bits: counterContract.id.toB256() }, INITIAL_OWNER: { Initialized: { Address: { bits: wallet.address.toB256() } }, }, }; const proxyContractFactory new Src14OwnedProxyFactory(wallet); const proxyDeploy await proxyContractFactory.deploy({ storageSlots, configurableConstants, }); const { contract: proxyContract } await proxyDeploy.waitForResult(); const { waitForResult } await proxyContract.functions .initialize_proxy() .call(); await waitForResult();initialize_proxy()调用完成并waitForResult解析后代理合约才算部署并初始化完毕。第三步通过代理合约 ID 调用合约关键约束实例化合约时只使用代理合约的 ID。代理 ID 在后续升级中保持不变这样升级后调用方代码无需改动import { Counter } from ./types; /** * 实例化时只用代理合约 ID * 即使未来升级它也是静态不变的 */ const proxiedContract new Counter(proxyContract.id, wallet); const incrementCall await proxiedContract.functions.increment_count(1).call(); await incrementCall.waitForResult(); const { value: count } await proxiedContract.functions.get_count().get(); console.log(count:, count.toNumber() 1);文档示例的输出是count: true表示通过代理调用increment_count(1)后读到的计数为 1示例结果供对照格式不是固定数值承诺。第四步部署 v2 并升级代理目标把合约改成 v2新增increments存储槽和get_increments()方法后升级分两步部署新版合约再调用代理的set_proxy_target把目标指向新合约 IDimport { CounterV2, CounterV2Factory } from ./types; const deployV2 await CounterV2Factory.deploy(wallet); const { contract: contractV2 } await deployV2.waitForResult(); const updateTargetCall await proxyContract.functions .set_proxy_target({ bits: contractV2.id.toB256() }) .call(); await updateTargetCall.waitForResult();然后仍用同一个代理合约 ID、换成 v2 的合约类重新实例化验证升级后的新方法可用/** * 仍然用同一个代理 ID 实例化 * 只是换成了新的合约类 */ const upgradedContract new CounterV2(proxyContract.id, wallet); const incrementCall2 await upgradedContract.functions .increment_count(1) .call(); await incrementCall2.waitForResult(); const { value: increments } await upgradedContract.functions .get_increments() .get(); const { value: count2 } await upgradedContract.functions.get_count().get(); console.log(secondCount, count2.toNumber() 2); console.log(increments, increments);文档示例输出为secondCount true和increments的值count2为 2 说明代理在目标切换后仍保留原有counter存储升级前累计 1升级后再 1 变成 2get_increments()能读到值说明 v2 新增的存储槽工作正常。限制与已知问题新增存储槽必须先初始化当新版本合约新增了存储槽时这些槽必须先通过代理合约写入初始化之后才能被读取否则交易会 revert。文档建议的做法是在代理合约上先对新存储槽执行一次写入。存储槽必须完整传递部署代理时漏传任何存储槽都会导致初始化不完整counterContractFactory.storageSlots.concat(Src14OwnedProxy.storageSlots)这一步不能省略。本文流程是手动实现的参考路径文档明确建议如果不需要自己控制每一步直接用fuels deploy命令部署和升级合约它会用同样的 SRC14 代理自动处理全部工作见 fuels CLI 命令文档。大合约以 blob 方式部署时耗时更长每个 blob 交易都要等待出块后再执行下一步部署与可交互之间的等待时间要相应放大。参考文件手动部署与升级代理合约指南合约部署指南typegen 示例代码生成类型文档【免费下载链接】fuels-tsFuel Network Typescript SDK项目地址: https://gitcode.com/GitHub_Trending/fu/fuels-ts创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表