部署 OpenZeppelin 可升级合约的正确方法是什么?
OpenZeppelin upgrdable 具有默认的 initialize() 函数,该函数由安全帽升级插件调用。
没有来自 hardhat-deploy 的自动调用,只有可以调用的升级后方法。
我应该先通过部署文件夹中的脚本部署合同,然后从部署文件夹外的某个脚本调用初始化函数吗? 或者有没有办法从部署文件夹本身合并调用初始化()函数?
hardhat-deploy 默认考虑部署脚本的方式是它们是幂等的。 这同样适用于代理部署。 这很适合开发/部署,因为您只需要考虑您想要成为什么状态,而 hardhat-deploy 将使一切都达到该状态,
所以为了回答你的问题,我通常做的是让我的初始化函数也是幂等的,所以如果它已经初始化,而不是抛出它,只需跳过它。 因此,我可以将该函数添加为 postUpgrade 函数并且一切正常。 在开发中,如果需要,我什至可以更改初始化函数以在升级时执行操作。
对于更复杂的场景,您可以在单独的部署脚本中组织每次升级。
有一个未记录的特性可能会发生变化: upgradeIndex
参数让您可以指定需要按顺序执行的部署脚本。
这非常适合测试,因为您可以在升级历史记录之间的任何时间点测试 yoru 代理并确保一切正常。
感谢您的快速回复。
看来我设法使所有工作顺利进行。 我使用deploy()
,然后使用execute
和 OpenZeppelin Initializable
基本合约的initialize
函数。 execute()
返回 true,这使它在我升级合约时无法运行:
// deploy/01_deploy_skin_rewards.js
module.exports = async ({
getNamedAccounts,
deployments,
}) => {
const {deploy} = deployments;
const {deployer} = await getNamedAccounts();
await deploy('SkinRewards', {
from: deployer,
proxy: {
owner: deployer,
proxyContract: 'OpenZeppelinTransparentProxy',
},
args: [],
log: true,
});
};
module.exports.tags = ['SkinRewards'];
接着
// deploy/02_init_skin_rewards.js
require('dotenv').config();
const RewardPeriods = ethers.BigNumber.from(process.env.REWARD_PERIODS);
module.exports = async ({getNamedAccounts, deployments}) => {
const { deployer } = await getNamedAccounts();
const SnookGame = await deployments.get('SnookGame');
const SnookState = await deployments.get('SnookState');
const SnookToken = await deployments.get('SnookToken');
const SkillToken = await deployments.get('SkillToken');
const Afterdeath = await deployments.get('Afterdeath');
const Treasury = await deployments.get('Treasury');
await deployments.execute(
'SkinRewards',
{from:deployer},
'initialize',
RewardPeriods,
Treasury.address,
SnookGame.address,
SnookState.address,
SnookToken.address,
SkillToken.address,
Afterdeath.address
);
deployments.log('Initialized SkinRewards');
return true;
};
module.exports.tags = ['initSkinRewards'];
module.exports.id = 'initSkinRewards';
这种方法可以被认为是_好的做法_吗? 是你如何寻找的吗?
正如我上面提到的,在SkinRewards
中,我继承了 OpenZeppelin 的Initializable
合同,该合同定义了首先调用的initialize()
函数。 所以我不知道如何在升级后运行它。 你可以解释吗? 谢谢
听起来不错,与使用methodName
("postUpgrade") 相比的缺点是您正在执行 2 tx 而不是 1
如果您使初始化函数具有幂等性,即可以通过确保进一步调用不会修改状态(如果已经初始化,则只需跳过),可以多次调用它而不会抛出,那么您可以通过methodName
选项使用它. 第一个部署也将执行它。
请注意,因为您使用参数并且部署功能已设置,因此您可以简单地关闭代理选项并拥有工作的不可变合约, methodName
选项使用args
选项作为参数。
这意味着代理实现的构造函数也需要有这些参数。 如果你从不打算在没有代理的情况下使用合约,你可以对参数不做任何事情。
实际上,为了方便起见,如果实现合同的 arg 构造函数为零,它会完全忽略构造函数,但我不确定这是一个好主意。
下面是带有methodName
选项的脚本:
// deploy/01_deploy_skin_rewards.js
module.exports = async ({
getNamedAccounts,
deployments,
}) => {
const {deploy} = deployments;
const {deployer} = await getNamedAccounts();
await deploy('SkinRewards', {
from: deployer,
proxy: {
methodName: 'initialize`,
owner: deployer,
proxyContract: 'OpenZeppelinTransparentProxy',
},
args: [
RewardPeriods,
Treasury.address,
SnookGame.address,
SnookState.address,
SnookToken.address,
SkillToken.address,
Afterdeath.address
],
log: true,
});
};
module.exports.tags = ['SkinRewards'];
_如果您使初始化函数具有幂等性,即可以通过确保进一步调用不会修改状态(如果已经初始化,则只需跳过),可以多次调用它而不会抛出,那么您可以通过 methodName 选项使用它。 第一个部署也会执行它。_
这意味着我们不能使用来自https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v4.0/contracts/proxy/utils/Initializable.sol的initialize()
函数原样.
@freebyte确实
我会考虑添加一个选项来获得这种行为
太好了谢谢!
另一个问题是关于使用已部署的合约。 特别是,我问的是Uniswap 。
从插件文档的这一部分,我看不到我如何提供已部署的 Uniswap 合约的地址。 Uniswap NPM 带有带有工件的build
文件夹,但我如何让插件了解特定地址的合约? 我们不想在部署脚本中使用以太,对吧?
以下代码不是我们在部署脚本中想要的:
const { ethers } = require("hardhat");
const UniswapV2FactoryArtifact = require('@uniswap/v2-core/build/UniswapV2Factory.json');
const UniswapV2FactoryAddress = '0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f';
module.exports = async ({getNamedAccounts, deployments}) => {
const {deployer} = await getNamedAccounts();
const UniswapFactory = await ethers.getContractAt(UniswapV2FactoryArtifact.abi, UniswapV2FactoryAddress);
// continue...
如何将 Uniswap 部署到deployments
var 中?
对于已经部署的合约,最好是在相应的部署文件夹中为它们创建文件
因此对于主网 uniswap 并假设您在 hardhat.config.js 中配置了主网网络,您可以创建文件“deployments/mainnet/UniswapFactory.json”。 至少它需要一个地址和 abi 字段
然后您可以在该网络上通过部署访问它
您指向的文档链接允许您访问工件(因此是 uniswap 构建文件夹)或部署脚本,但后者仅在相关项目在某处暴露这些部署脚本时才有效
我在我自己的项目中这样做,例如https://github.com/wighawag/universal-forwarder ,它允许用户简单地安装 npm 包,然后他们将整个项目部署在他们的测试或他们自己的网络上。 但是 uniswap 没有提供
伟大的。 非常感谢。
重新初始化代理功能
我在 hardhat-deploy 0.8.0 中添加了一个新选项(而不是仍然可用的methodName
)
如果您遇到任何问题,请告诉我
最有用的评论
重新初始化代理功能
我在 hardhat-deploy 0.8.0 中添加了一个新选项(而不是仍然可用的
methodName
)见https://github.com/wighawag/template-ethereum-contracts/blob/595c1b5ec9cdf1276f4d3a43b4825bcef78bd2cd/deploy/004_deploy_erc20_always_proxied_via_openzeppelin_proxy.ts#L12 -L26
如果您遇到任何问题,请告诉我