与 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 文档。