OpenZeppelinのアップグレード可能なコントラクトをデプロイする正しい方法は何ですか?
OpenZeppelin upgrdableには、hardhatアップグレードプラグインによって呼び出されるデフォルトのinitialize()関数があります。
hardhat-deployからの自動呼び出しはなく、呼び出される可能性があるのはアップグレード後のメソッドのみです。
最初にデプロイフォルダー内のスクリプトを介してコントラクトをデプロイしてから、デプロイフォルダー外のスクリプトから初期化関数を呼び出す必要がありますか? または、デプロイフォルダ自体から呼び出しているinitialize()関数を組み込む方法はありますか?
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
では、最初に呼び出されるinitialize()
関数を定義するOpenZeppelinのInitializable
コントラクトから継承します。 そのため、アップグレード後に実行する方法がわかりません。 説明できますか? ありがとう
methodName
( "postUpgrade")を使用する場合と比較した場合の欠点は、1ではなく2txを実行していることです。
初期化関数をべき等にする場合、つまり、それ以降の呼び出しで状態が変更されないようにすることで(すでに初期化されている場合はスキップすることで)、スローせずに複数回呼び出すことができます。その後、 methodName
オプションを使用して使用できます。 。 最初のデプロイメントでもそれが実行されます。
引数を使用し、デプロイ関数が設定されているため、プロキシオプションをオフにして不変のコントラクトを機能させることができるため、 methodName
$オプションは引数にargs
オプションを使用することに注意してください。
これは、プロキシ実装のコンストラクターにもこれらの引数が必要であることを意味します。 プロキシなしでコントラクトを使用するつもりがない場合は、引数に対して何もできません。
実際、便宜上、実装コントラクトの引数がゼロの場合、コンストラクターは完全に無視されますが、それが良いアイデアかどうかはわかりません。
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確かに
この種の動作を取得するためのオプションを追加することを検討します
よかった。ありがとう!
もう1つの質問は、デプロイされたコントラクトの使用についてです。 特に、ユニスワップのリガーリングをお願いしています。
プラグインドキュメントのこの部分から、デプロイされたユニスワップコントラクトのアドレスをどのように提供するかがわかりません。 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にするにはどうすればよいですか?
すでにデプロイされているコントラクトの場合、対応するデプロイメントフォルダーにそれらのファイルを作成するのが最善です。
したがって、メインネットユニスワップの場合、hardhat.config.jsでメインネットネットワークが構成されていると仮定すると、ファイル「deployments / mainnet /UniswapFactory.json」を作成できます。 少なくとも、アドレスとabiフィールドが必要です
次に、そのネットワーク上にあるときにデプロイメントを介してアクセスできます
アーティファクト(つまり、ユニスワップビルドフォルダー)にアクセスしたり、スクリプトをデプロイしたりできるようにするために指定したドキュメントリンクですが、後者は、問題のプロジェクトがこれらのデプロイスクリプトをどこかに公開した場合にのみ機能します
これは、 https://github.com/wighawag/universal-forwarderのような自分のプロジェクトで行います。これにより、ユーザーはnpmパッケージをインストールするだけで、プロジェクト全体をテストまたは独自のネットワークにデプロイできます。 しかし、ユニスワップはそれを提供しません
すごい。 どうもありがとう。
プロキシの関数を再初期化します
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を参照してください
問題が発生した場合はお知らせください