版本更新后先检查哪些内容
把模型输出接进可升级合约,最容易犯的错误是把“升级验证通过”当成整次发布的通行证。它只说明某一部分检查没有报错。合约升级、模型给出的交易参数、签名权限和线上观察,本来就是几条不同的线,必须分别过关。
先说合约升级。代理合约的状态留在代理地址,新实现能否接手,取决于存储布局、继承关系、初始化逻辑和升级权限。已有变量改了顺序或类型,风险往往不是立刻显现,而是某个旧接口继续被调用时才暴露。靠人工对着源码比对很难覆盖编译产物和继承链,项目已经采用 OpenZeppelin Upgrades 时,应该沿用它的验证流程。这里的验证不会发交易:
import { ethers, upgrades } from "hardhat"; export async function validateUpgrade(proxyAddress: string) { const next = await ethers.getContractFactory("NextVaultImplementation"); await upgrades.validateUpgrade(proxyAddress, next, { kind: "uups" }); }proxyAddress、代理类型和合约工厂名都要来自当前网络的部署配置。验证报错时,发布就在这里停下;验证通过后,还要确认初始化函数不会被再次调用、谁有升级权,以及旧 ABI 的关键读写是否仍然成立。若要回退,同样要走布局检查,不能因为“只是换回旧实现”就省略步骤。
模型输出是另一类外部输入。JSON 能解析,不代表其中的目标地址、金额和函数调用符合业务规则。把模型生成的对象直接交给合约,是把最敏感的一段边界跳过去了。第一层可以做严格的结构校验,拒绝多余字段、科学计数法金额和不在允许范围内的函数名:
import { z } from "zod"; import { utils } from "ethers"; const requestSchema = z.object({ target: z.string().refine(utils.isAddress, "目标地址无效"), method: z.enum(["swap", "rebalance", "exit"]), amountWei: z.string().regex(/^\d+$/, "金额必须是整数字符串"), minOutWei: z.string().regex(/^\d+$/, "最小获得量必须是整数字符串"), }).strict(); export function parseRequest(raw: string) { return requestSchema.safeParse(JSON.parse(raw)); }这层只解决形状问题。下一层还应核对链 ID、协议白名单、代币对、授权额度、金额上限和用户本次确认的意图。结构合法的swap也可能指向错误网络或超过用户允许的范围。校验失败后,调用方必须明确终止,不能为了“提高成功率”改走一个未受保护的发送路径。日志只记录失败类别和请求标识,原始模型内容、签名材料和完整交易参数不应随手写进去。
升级前后的回归测试,最好固定在一个可追溯的区块高度上做分叉。这样能复用当时的链上余额、授权和外部协议状态,测试结果也更容易复现。分叉不是主网的缩小版:它不会重演后续预言机变化、内存池竞争、重组或拥堵。因此它适合检查状态和调用路径,不适合替代线上风险判断。
import { network } from "hardhat"; export async function resetFork(rpcUrl: string, blockNumber: number) { if (!rpcUrl || !Number.isSafeInteger(blockNumber) || blockNumber <= 0) { throw new Error("分叉配置无效"); } await network.provider.request({ method: "hardhat_reset", params: [{ forking: { jsonRpcUrl: rpcUrl, blockNumber } }], }); }测试断言应来自合约自己的不变量,而不是照搬一组通用字段。某些协议要求总供应量不变,另一些协议允许铸造或销毁;有的升级更关心债务、抵押率或角色权限。除正常路径外,还应覆盖授权不足、暂停状态、最小产出不满足和外部调用失败。RPC 地址、区块高度、实现版本和测试夹具版本都应写入结果,之后有人复查才知道当时测的是什么。
最后是发布后的门槛。把合约调用失败、模型校验失败、用户拒绝、签名失败和广播失败混成一个成功率,出了问题也很难定位。应分别记录它们,并按同一类方法、相近输入观察 gas 和耗时。模型推理、策略校验、签名、RPC 广播是不同阶段,端到端平均值不能替它们做诊断。
阈值没有放之四海皆准的数字,应该以旧版本基线、实际样本和资产风险来定,并写进发布配置。上线记录至少保留代理地址、实现字节码、编译器设置、验证结果、分叉区块和批准人。链下服务可以回滚,已经确认上链的交易不能撤销;这个差别决定了涉及资产和权限的动作应保守一些,把不可逆步骤留给明确的授权流程。