Hardhat-deploy: Вопрос по развертыванию обновляемых контрактов

Созданный на 8 июн. 2021  ·  9Комментарии  ·  Источник: wighawag/hardhat-deploy

Как правильно развернуть обновляемые контракты OpenZeppelin?
OpenZeppelin upgrdable имеет функцию initialize() по умолчанию, которая вызывается плагином обновления каски.
Нет автоматического вызова из hardhat-deploy, только метод после обновления, который может быть вызван.

Должен ли я сначала развертывать контракты через сценарии в папке развертывания, а затем вызывать функции инициализации из некоторого сценария вне папки развертывания? Или есть способ включить вызов функции initialize() из самой папки развертывания?

Самый полезный комментарий

Функция повторной инициализации для прокси

Я добавил новые параметры (вместо methodName , которые все еще доступны) в hardhat-deploy 0.8.0.

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

Дайте мне знать, если у вас возникнут какие-либо проблемы

Все 9 Комментарий

Способ, которым hardhat-deploy считает развертывание сценария по умолчанию, заключается в том, что они являются идемпотентными. То же самое относится и к развертыванию прокси. Это делает его удобным для разработки/развертывания, так как вам нужно только подумать о том, в каком состоянии вы хотите быть, и hardhat-deploy сделает все, чтобы достичь этого состояния,

поэтому, чтобы ответить на ваш вопрос, я обычно делаю свою функцию инициализации идемпотентной, поэтому вместо того, чтобы выбрасывать ее, просто пропустите, если она уже инициализирована. Таким образом, я могу добавить эту функцию в качестве функции postUpgrade, и все будет работать нормально. В процессе разработки я могу даже изменить функцию инициализации, чтобы она выполняла какие-либо действия при обновлении, если это необходимо.

Для более сложного сценария вы можете организовать каждое обновление в отдельном сценарии развертывания.

Существует недокументированная функция, которая может быть изменена: параметр upgradeIndex , позволяющий указать сценарий развертывания, который необходимо выполнить по порядку.

Это отлично подходит для тестирования, так как вы можете протестировать свой прокси-сервер в любой момент между историей обновлений и убедиться, что все работает.

Спасибо за ваш быстрый ответ.
Кажется, мне удалось заставить все работать гладко. Я использую deploy() , а затем execute с функцией $# initialize Initializable . 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 в качестве аргумента.
Это означает, что конструктор реализации прокси также должен иметь эти аргументы. Если вы никогда не собираетесь использовать контракт без прокси, вы можете просто ничего не делать с аргументами.

На самом деле для удобства, если контракт реализации имеет конструкцию с нулевым аргументом, он полностью игнорирует конструктор, но я не уверен, что это была хорошая идея.

Вот сценарий с опцией 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. первое развертывание также выполнит его._

Это означает, что мы не можем использовать функцию initialize() AS-IS из https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v4.0/contracts/proxy/utils/Initializable.sol . .

@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 не обеспечивает этого

Здорово. Большое спасибо.

Функция повторной инициализации для прокси

Я добавил новые параметры (вместо methodName , которые все еще доступны) в hardhat-deploy 0.8.0.

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

Дайте мне знать, если у вас возникнут какие-либо проблемы

Была ли эта страница полезной?
0 / 5 - 0 рейтинги