Hardhat-deploy: Question sur le déploiement de contrats évolutifs

Créé le 8 juin 2021  ·  9Commentaires  ·  Source: wighawag/hardhat-deploy

Quelle est la bonne façon de déployer des contrats évolutifs OpenZeppelin ?
OpenZeppelin upgrdable a la fonction initialize () par défaut qui est appelée par le plugin de mise à niveau du casque.
Il n'y a pas d'appel automatique depuis hardhat-deploy, seule la méthode post-mise à niveau peut être appelée.

Dois-je d'abord déployer les contrats via les scripts du dossier de déploiement, puis appeler les fonctions d'initialisation à partir d'un script en dehors du dossier de déploiement ? Ou existe-t-il un moyen d'incorporer l'appel de la fonction initialize() à partir du dossier de déploiement lui-même ?

Commentaire le plus utile

Réinitialiser la fonction pour les proxys

J'ai ajouté une nouvelle option (au lieu de methodName qui est toujours disponible) dans hardhat-deploy 0.8.0

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

Faites-moi savoir si vous rencontrez des problèmes

Tous les 9 commentaires

La façon dont hardhat-deploy considère le script de déploiement par défaut est qu'ils sont idempotents. Il en va de même pour le déploiement de proxy. Cela le rend agréable pour le développement/déploiement car vous n'avez qu'à penser à l'état que vous voulez être et le déploiement du casque fera tout pour atteindre cet état,

donc pour répondre à votre question, ce que je fais habituellement est de rendre ma fonction d'initialisation aussi idempotente, donc au lieu de la lancer, sautez simplement si elle est déjà initialisée. Je peux donc ajouter cette fonction en tant que fonction postUpgrade et tout fonctionne bien. En développement, je peux même modifier la fonction d'initialisation pour faire des choses lorsque je mets à niveau si nécessaire.

Pour un scénario plus complexe, vous pouvez organiser chaque mise à niveau dans un script de déploiement distinct.

Il existe une fonctionnalité non documentée qui est sujette à modifications : le paramètre upgradeIndex qui vous permet de spécifier le script de déploiement qui doit être exécuté dans l'ordre.

C'est idéal pour les tests car vous pouvez tester votre proxy à tout moment entre l'historique de mise à niveau et vous assurer que tout fonctionne.

Merci pour votre réponse rapide.
Il semble que j'ai réussi à faire en sorte que tout fonctionne correctement. J'utilise deploy() puis execute avec la fonction initialize du contrat de base OpenZeppelin Initializable . Le execute() renvoie true et cela l'empêche de s'exécuter lorsque je mets à niveau les contrats :

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

puis

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

Cette méthode peut-elle être considérée comme une _bonne pratique_ ? Est-ce ainsi que vous l'avez recherché ?

Comme je l'ai mentionné ci-dessus, dans SkinRewards j'hérite du Initializable d'OpenZeppelin qui définit la fonction initialize() à appeler en premier. Je ne vois donc pas comment je peux l'exécuter après la mise à niveau. Peux-tu expliquer? Merci

Cela semble bien, l'inconvénient par rapport à l'utilisation methodName ("postUpgrade") est que vous exécutez 2 tx au lieu de 1

Si vous rendez votre fonction d'initialisation idempotente, c'est-à-dire qu'elle peut être appelée plusieurs fois sans lancer en s'assurant qu'un nouvel appel ne modifiera pas l'état (en sautant simplement si elle est déjà initialisée), alors vous pouvez l'utiliser via les options methodName . le premier déploiement l'exécutera également.

Notez que, comme vous utilisez des arguments et que la fonction de déploiement est configurée pour que vous puissiez simplement désactiver l'option proxy et avoir un contrat immuable fonctionnel, l'option methodName utilise les options args comme argument.
Cela signifie que le constructeur de l'implémentation du proxy doit également avoir ces arguments. Si vous n'avez jamais l'intention d'utiliser le contrat sans procuration, vous pouvez simplement ne rien faire avec les arguments.

En fait, pour plus de commodité, si le contrat d'implémentation a une construction à zéro argument, il ignore entièrement le constructeur, mais je ne suis pas sûr que ce soit une bonne idée.

Voici le script avec l'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'];

_Si vous rendez votre fonction d'initialisation idempotente, c'est-à-dire qu'elle peut être appelée plusieurs fois sans lancer en s'assurant que d'autres appels ne modifieront pas l'état (en sautant simplement si elle est déjà initialisée), alors vous pouvez l'utiliser via les options methodName. le premier déploiement l'exécutera également._

Cela signifie que nous ne pouvons pas utiliser la fonction initialize() AS-IS de https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v4.0/contracts/proxy/utils/Initializable.sol .

@freebyte en effet

Je vais envisager d'ajouter une option pour obtenir ce genre de comportement

Super merci!

Une autre question concerne l'utilisation des contrats déployés. En particulier, je demande concernant Uniswap .

À partir de cette partie de la documentation du plugin, je ne vois pas comment je fournis l'adresse du contrat Uniswap déployé. Uniswap NPM est livré avec un dossier build avec les artefacts, mais comment puis-je faire en sorte que le plugin connaisse le contrat à une adresse spécifique ? Nous ne voulons pas utiliser d'éthers dans le script de déploiement, n'est-ce pas ?

Le code suivant n'est PAS ce que nous voulons dans le script de déploiement :

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

Comment puis-je obtenir le déploiement d'Uniswap dans deployments var ?

pour les contrats déjà déployés, le mieux est de créer des fichiers pour eux dans le dossier de déploiements correspondant
donc pour mainnet uniswap et en supposant que vous avez un réseau mainnet configuré dans hardhat.config.js, vous pouvez créer le fichier "deployments/mainnet/UniswapFactory.json". Au minimum, il a besoin d'une adresse et d'un champ abi

alors vous pouvez y accéder via des déploiements lorsque vous êtes sur ce réseau

Le lien doc que vous pointez pour vous permettre d'accéder aux artefacts (donc au dossier de construction uniswap) ou au script de déploiement, mais ce dernier ne fonctionne que si le projet en question a exposé ces scripts de déploiement quelque part

Je le fais dans mon propre projet comme https://github.com/wighawag/universal-forwarder qui permet à l'utilisateur d'installer simplement le package npm et ils obtiennent l'ensemble du projet déployé dans leur test ou sur leur propre réseau. Mais uniswap ne fournit pas cela

Super. Merci beaucoup.

Réinitialiser la fonction pour les proxys

J'ai ajouté une nouvelle option (au lieu de methodName qui est toujours disponible) dans hardhat-deploy 0.8.0

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

Faites-moi savoir si vous rencontrez des problèmes

Cette page vous a été utile?
0 / 5 - 0 notes

Questions connexes

gitpusha picture gitpusha  ·  6Commentaires

lepidotteri picture lepidotteri  ·  5Commentaires

smartcontracts picture smartcontracts  ·  20Commentaires

lcswillems picture lcswillems  ·  14Commentaires

jaypaik picture jaypaik  ·  13Commentaires