与 Hardhat 一起使用
这个包向你的 Hardhat 脚本添加了函数,以便你可以部署和升级你的合约的代理。依赖于 ethers.js
。
提示:查看 分步教程,展示了从创建、测试和部署,一直到使用 Gnosis Safe 进行升级的整个过程。
安装
$ npm install --save-dev @openzeppelin/hardhat-upgrades
$ npm install --save-dev @nomicfoundation/hardhat-ethers ethers # peer dependencies
并在你的 hardhat.config.js
中注册插件:
require('@openzeppelin/hardhat-upgrades');
在脚本中使用
代理
你可以在 Hardhat 脚本中使用此插件,通过 deployProxy
函数部署你的合约的可升级实例:
// scripts/create-box.js
const { ethers, upgrades } = require("hardhat");
async function main() {
const Box = await ethers.getContractFactory("Box");
const box = await upgrades.deployProxy(Box, [42]);
await box.waitForDeployment();
console.log("Box deployed to:", await box.getAddress());
}
main();
这将自动检查 Box
合约是否是升级安全的,为 Box
合约部署一个实现合约(除非之前已经部署过一个),创建一个代理(如果需要,还会创建一个代理管理员),并通过调用 initialize(42)
来初始化它。
然后,在另一个脚本中,你可以使用 upgradeProxy
函数将已部署的实例升级到新版本。新版本可以是一个不同的合约(例如 BoxV2
),或者你可以仅修改现有的 Box
合约并重新编译它 - 插件会注意到它已更改。
// scripts/upgrade-box.js
const { ethers, upgrades } = require("hardhat");
async function main() {
const BoxV2 = await ethers.getContractFactory("BoxV2");
const box = await upgrades.upgradeProxy(BOX_ADDRESS, BoxV2);
console.log("Box upgraded");
}
main();
注意:虽然此插件会跟踪你在每个网络上部署的所有实现合约,以便复用它们并验证存储兼容性,但它_不_跟踪你已部署的代理。这意味着你需要手动跟踪每个部署地址,以便在需要时将它们提供给升级函数。
该插件将负责比较 BoxV2
与前一个版本,以确保它们兼容升级,部署新的 BoxV2
实现合约(除非之前已经部署过一个),并将现有代理升级到新的实现。
信标代理
你也可以使用此插件通过 deployBeacon
函数为你的合约部署一个可升级的信标,然后使用 deployBeaconProxy
函数部署一个或多个指向它的信标代理。
// scripts/create-box.js
const { ethers, upgrades } = require("hardhat");
async function main() {
const Box = await ethers.getContractFactory("Box");
const beacon = await upgrades.deployBeacon(Box);
await beacon.waitForDeployment();
console.log("Beacon deployed to:", await beacon.getAddress());
const box = await upgrades.deployBeaconProxy(beacon, Box, [42]);
await box.waitForDeployment();
console.log("Box deployed to:", await box.getAddress());
}
main();
然后,在另一个脚本中,你可以使用 upgradeBeacon
函数将信标升级到新版本。当信标升级后,所有指向它的信标代理将使用新的合约实现。
// scripts/upgrade-box.js
const { ethers, upgrades } = require("hardhat");
async function main() {
const BoxV2 = await ethers.getContractFactory("BoxV2");
await upgrades.upgradeBeacon(BEACON_ADDRESS, BoxV2);
console.log("Beacon upgraded");
const box = BoxV2.attach(BOX_ADDRESS);
}
main();
在测试中使用
如果想要为合约的升级添加测试(你应该这样做!),你可以从 Hardhat 测试中使用插件的函数。API 与脚本中的相同。
代理
const { expect } = require("chai");
describe("Box", function() {
it('works', async () => {
const Box = await ethers.getContractFactory("Box");
const BoxV2 = await ethers.getContractFactory("BoxV2");
const instance = await upgrades.deployProxy(Box, [42]);
const upgraded = await upgrades.upgradeProxy(await instance.getAddress(), BoxV2);
const value = await upgraded.value();
expect(value.toString()).to.equal('42');
});
});
信标代理
const { expect } = require("chai");
describe("Box", function() {
it('works', async () => {
const Box = await ethers.getContractFactory("Box");
const BoxV2 = await ethers.getContractFactory("BoxV2");
const beacon = await upgrades.deployBeacon(Box);
const instance = await upgrades.deployBeaconProxy(beacon, Box, [42]);
await upgrades.upgradeBeacon(beacon, BoxV2);
const upgraded = BoxV2.attach(await instance.getAddress());
const value = await upgraded.value();
expect(value.toString()).to.equal('42');
});
});
与 Defender 一起使用
如果你正在使用 OpenZeppelin Defender,请参阅 OpenZeppelin Defender 与 Hardhat,了解如何将其用于部署。
API
请参阅 Hardhat Upgrades API,获得完整的 API 文档。