Hardhat-deploy: Frage zum Bereitstellen aktualisierbarer Verträge

Erstellt am 8. Juni 2021  ·  9Kommentare  ·  Quelle: wighawag/hardhat-deploy

Was ist der richtige Weg, um aktualisierbare OpenZeppelin-Verträge bereitzustellen?
OpenZeppelin aktualisierbar hat die Standardfunktion initialize(), die vom Plugin für Hardhat-Upgrades aufgerufen wird.
Es gibt keinen automatischen Aufruf von Hardhat-Deploy, nur eine Post-Upgrade-Methode, die aufgerufen werden kann.

Soll ich die Verträge zuerst über die Skripts im Bereitstellungsordner bereitstellen und dann Initialisierungsfunktionen von einem Skript außerhalb des Bereitstellungsordners aufrufen? Oder gibt es eine Möglichkeit, den Aufruf der Funktion initialize() aus dem Bereitstellungsordner selbst zu integrieren?

Hilfreichster Kommentar

Funktion für Proxys neu initialisieren

Ich habe in Hardhat-Deploy 0.8.0 eine neue Option hinzugefügt (anstelle von methodName , die noch verfügbar ist).

siehe https://github.com/wighawag/template-ethereum-contracts/blob/595c1b5ec9cdf1276f4d3a43b4825bcef78bd2cd/deploy/004_deploy_erc20_always_proxyed_via_openzeppelin_proxy.ts#L12 -L26

Lassen Sie mich wissen, wenn Sie auf Probleme stoßen

Alle 9 Kommentare

Die Art und Weise, wie hardhat-deploy das Bereitstellungsskript standardmäßig betrachtet, ist, dass sie idempotent sind. Dasselbe gilt für die Proxy-Bereitstellung. Dies macht es für die Entwicklung/Bereitstellung angenehm, da Sie nur darüber nachdenken müssen, welchen Zustand Sie erreichen möchten, und Hardhat-Deploy wird alles tun, um diesen Zustand zu erreichen.

Um Ihre Frage zu beantworten, mache ich normalerweise meine Initialisierungsfunktion auch idempotent, anstatt sie einfach zu überspringen, wenn sie bereits initialisiert ist. Ich kann diese Funktion also als postUpgrade-Funktion hinzufügen und alles funktioniert gut. In der Entwicklung kann ich bei Bedarf sogar die Initialisierungsfunktion ändern, um Dinge zu tun, wenn ich ein Upgrade durchführe.

Für komplexere Szenarien können Sie jedes Upgrade in einem separaten Bereitstellungsskript organisieren.

Es gibt eine undokumentierte Funktion, die Änderungen unterliegt: der Parameter upgradeIndex , mit dem Sie das Bereitstellungsskript angeben können, das der Reihe nach ausgeführt werden muss.

Dies eignet sich hervorragend zum Testen, da Sie Ihren Proxy jederzeit zwischen dem Upgrade-Verlauf testen und sicherstellen können, dass alles funktioniert.

Danke für deine schnelle Antwort.
Es scheint, dass ich es geschafft habe, dass alles reibungslos funktioniert. Ich benutze deploy() und dann execute mit initialize Funktion von OpenZeppelin Initializable Basisvertrag. Das execute() gibt true zurück und das führt dazu, dass es nicht ausgeführt wird, wenn ich die Verträge aktualisiere:

// 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'];

und dann

// 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';

Kann diese Methode als _gute Praxis_ betrachtet werden? Ist es so, wie Sie es gesucht haben?

Wie ich oben erwähnt habe, erbe ich in SkinRewards von OpenZeppelins Initializable -Vertrag, der die Funktion initialize() definiert, die zuerst aufgerufen werden soll. Ich sehe also nicht, wie ich es nach dem Upgrade ausführen kann. Können Sie erklären? Danke

Das hört sich gut an, der Nachteil gegenüber der Verwendung methodName ("postUpgrade") ist, dass Sie 2 tx statt 1 ausführen

Wenn Sie Ihre Initialisierungsfunktion idempotent machen, d. h. sie kann mehrmals aufgerufen werden, ohne dass sie ausgelöst wird, indem Sie sicherstellen, dass ein weiterer Aufruf den Status nicht ändert (indem Sie einfach überspringen, wenn sie bereits initialisiert ist), können Sie sie über die methodName -Optionen verwenden . die erste Bereitstellung wird es auch ausführen.

Beachten Sie, dass die methodName -Option die args -Optionen für Argumente verwendet, da Sie Argumente verwenden und die Bereitstellungsfunktion so eingerichtet ist, dass Sie die Proxy-Option einfach ausschalten und einen funktionierenden unveränderlichen Vertrag haben können.
Das bedeutet, dass der Konstruktor der Proxy-Implementierung diese Argumente ebenfalls haben muss. Wenn Sie niemals beabsichtigen, den Vertrag ohne Proxy zu verwenden, können Sie mit den Argumenten einfach nichts anfangen.

Der Einfachheit halber ignoriert der Implementierungsvertrag den Konstruktor vollständig, wenn der Implementierungsvertrag ein Null-Arg-Konstrukt hat, aber ich bin mir nicht sicher, ob es eine gute Idee war.

Hier wäre das Skript mit der Option 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'];

_Wenn Sie Ihre Initialisierungsfunktion idempotent machen, das heißt, sie kann mehrmals aufgerufen werden, ohne zu werfen, indem Sie sicherstellen, dass ein weiterer Aufruf den Status nicht ändert (indem Sie einfach überspringen, wenn sie bereits initialisiert ist), dann können Sie sie über die methodName-Optionen verwenden. die erste Bereitstellung wird es auch ausführen._

Das bedeutet, dass wir die Funktion initialize() AS-IS von https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v4.0/contracts/proxy/utils/Initializable.sol nicht verwenden können .

@freebyte in der Tat

Ich werde erwägen, eine Option hinzuzufügen, um diese Art von Verhalten zu erhalten

Grosses Dankeschön!

Eine andere Frage betrifft die Verwendung von bereitgestellten Verträgen. Insbesondere frage ich nach Uniswap .

In diesem Teil der Plugin- Dokumentation sehe ich nicht, wie ich die Adresse des bereitgestellten Uniswap-Vertrags liefere. Uniswap NPM wird mit dem Ordner build mit den Artefakten geliefert, aber wie bringe ich das Plugin dazu, den Vertrag an einer bestimmten Adresse zu kennen? Wir wollen keine Ether im Bereitstellungsskript verwenden, richtig?

Der folgende Code ist NICHT das, was wir im Bereitstellungsskript wollen:

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...

Wie erhalte ich die Uniswap-Bereitstellung in deployments var?

Für bereits bereitgestellte Verträge ist es am besten, Dateien für sie im entsprechenden Bereitstellungsordner zu erstellen
also für Mainnet Uniswap und vorausgesetzt, Sie haben ein Mainnet-Netzwerk in hardhat.config.js konfiguriert, können Sie die Datei „deployments/mainnet/UniswapFactory.json“ erstellen. Es benötigt mindestens eine Adresse und ein abi-Feld

dann können Sie über Bereitstellungen darauf zugreifen, wenn Sie sich in diesem Netzwerk befinden

Der Dokumentlink, auf den Sie verweisen, ermöglicht Ihnen den Zugriff auf Artefakte (also den Uniswap-Build-Ordner) oder das Bereitstellungsskript, aber letzteres funktioniert nur, wenn das betreffende Projekt dieses Bereitstellungsskript irgendwo verfügbar macht

Ich mache das in meinem eigenen Projekt wie https://github.com/wighawag/universal-forwarder , das es Benutzern ermöglicht, einfach das npm-Paket zu installieren, und sie bekommen das gesamte Projekt in ihrem Test oder in ihrem eigenen Netzwerk bereitgestellt. Aber Uniswap bietet das nicht

Toll. Vielen Dank.

Funktion für Proxys neu initialisieren

Ich habe in Hardhat-Deploy 0.8.0 eine neue Option hinzugefügt (anstelle von methodName , die noch verfügbar ist).

siehe https://github.com/wighawag/template-ethereum-contracts/blob/595c1b5ec9cdf1276f4d3a43b4825bcef78bd2cd/deploy/004_deploy_erc20_always_proxyed_via_openzeppelin_proxy.ts#L12 -L26

Lassen Sie mich wissen, wenn Sie auf Probleme stoßen

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen