You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将预编译合约部署到Hardhat节点并仅用ABI/Bytecode升级代理合约

错误原因

OpenZeppelin 的 hardhat-upgrades 插件执行 upgradeProxy 时,默认会自动校验新老合约的存储布局兼容性,避免升级后出现存储冲突。这个校验逻辑依赖合约编译阶段生成的AST、存储布局元数据,这些数据默认存储在 cache 文件夹和 artifacts 文件夹下对应合约的JSON文件中,并不是仅靠ABI和Bytecode就能完成的。你删除合约源文件后重新执行编译命令,会清空旧的缓存和产物,插件找不到对应合约的元数据,就会抛出找不到合约的错误。

可行解决方案

方案1:跳过存储布局校验(适合已经自行确认存储兼容的场景)

如果你已经人工确认新合约的存储布局和旧合约完全兼容,不会出现变量覆盖、顺序错乱等问题,可以在调用 upgradeProxy 时显式开启跳过校验的参数,就不需要依赖本地缓存的元数据,也不需要保留源文件:

const NFTv2 = await ethers.getContractFactory(upgradedContract.abi, upgradedContract.bytecode);
// 新增unsafeSkipStorageCheck参数跳过存储校验
contract = await upgrades.upgradeProxy(proxyContract.address, NFTv2, {
  unsafeSkipStorageCheck: true
});

⚠️ 注意:该操作存在风险,必须自行确保新合约的存储变量定义顺序、类型和旧合约完全兼容,新增变量必须放在所有旧变量之后。

方案2:手动部署实现合约后执行升级

完全不依赖hardhat-upgrades插件的封装逻辑,只用ABI和Bytecode完成部署和升级,步骤如下:

  1. 部署新的实现合约
const NFTv2 = await ethers.getContractFactory(upgradedContract.abi, upgradedContract.bytecode);
const newImpl = await NFTv2.deploy();
await newImpl.deployed();
  1. 根据你使用的代理类型调用升级方法:
  • 如果是透明代理(Transparent Proxy):用代理管理员地址调用代理的upgradeTo方法
// 这里proxyAbi是代理合约的ABI,包含upgradeTo方法
const proxy = await ethers.getContractAt(proxyAbi, proxyContract.address, adminSigner);
await proxy.upgradeTo(newImpl.address);
  • 如果是UUPS代理:调用代理的升级方法(通常需要权限校验)
// 这里的abi包含你合约里定义的升级方法,比如upgradeToAndCall
const proxy = await ethers.getContractAt(upgradedContract.abi, proxyContract.address, adminSigner);
await proxy.upgradeToAndCall(newImpl.address, "0x");

方案3:保留编译产物元数据

如果不想跳过校验,可以在第一次编译完新版本合约之后,备份artifacts/contracts/下对应合约的JSON文件,以及cache文件夹下的相关元数据,后续执行升级时不要执行npx hardhat clean或者删除源文件后重新编译,直接使用备份的产物即可,不需要保留.sol源文件在contracts目录下。

内容的提问来源于stack exchange,提问作者johnhckuo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.09 01:51:01