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 ?
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
Faites-moi savoir si vous rencontrez des problèmes
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.0voir 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