Hardhat-deploy: Pergunta sobre a implantação de contratos atualizáveis

Criado em 8 jun. 2021  ·  9Comentários  ·  Fonte: wighawag/hardhat-deploy

Qual é a maneira correta de implantar contratos atualizáveis ​​do OpenZeppelin?
O OpenZeppelin upgrdable tem a função default initialize() que é chamada pelo plug-in de atualizações do capacete de segurança.
Não há chamada automática do hardhat-deploy, apenas o método pós-atualização que pode ser chamado.

Devo implantar os contratos primeiro por meio dos scripts na pasta de implantação e, em seguida, chamar as funções de inicialização de algum script fora da pasta de implantação? Ou existe uma maneira de incorporar a função de chamada initialize() da própria pasta de implantação?

Comentários muito úteis

Reinicializar a função para proxies

Eu adicionei novas opções (em vez de methodName que ainda está disponível) no hardhat-deploy 0.8.0

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

Deixe-me saber se você encontrar algum problema

Todos 9 comentários

A maneira como o hardhat-deploy considera o script de implantação por padrão é que eles são idempotentes. O mesmo se aplica à implantação de proxy. Isso o torna bom para desenvolvimento/implantação, pois você só precisa pensar em qual estado deseja estar e o hardhat-deploy fará tudo para atingir esse estado,

então, para responder à sua pergunta, o que eu costumo fazer é tornar minha função de inicialização idempotente também, então, em vez de jogá-la, simplesmente pule se ela já estiver inicializada. Assim, posso adicionar essa função como a função postUpgrade e tudo funciona bem. Em desenvolvimento, posso até alterar a função de inicialização para fazer as coisas quando eu atualizar, se necessário.

Para cenários mais complexos, você pode organizar cada atualização em um script de implantação separado.

Há um recurso não documentado que está sujeito a alterações: o parâmetro upgradeIndex que permite especificar o script de implantação que precisa ser executado em ordem.

Isso é ótimo para teste, pois você pode testar seu proxy em qualquer ponto entre o histórico de atualização e garantir que tudo funcione.

Obrigado pela sua resposta rápida.
Parece que consegui fazer tudo funcionar sem problemas. Eu uso deploy() e então execute com a função initialize #$2$#$ do contrato base do OpenZeppelin Initializable . O execute() retorna true e isso faz com que ele não seja executado quando eu atualizo os contratos:

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

e então

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

Este método pode ser considerado _boa prática_? É como você buscou?

Como mencionei acima, em SkinRewards eu herdo do contrato Initializable do OpenZeppelin que define a função initialize() a ser chamada primeiro. Então, não vejo como posso executá-lo após a atualização. Você pode explicar? Obrigado

Isso parece bom, a desvantagem em relação ao uso de methodName ("postUpgrade") é que você está executando 2 tx em vez de 1

Se você tornar sua função de inicialização idempotente, ou seja, ela pode ser chamada várias vezes sem lançar, certificando-se de que outras chamadas não modifiquem o estado (simplesmente pulando se já inicializadas), então você pode usá-la através das opções methodName . a primeira implantação também a executará.

Observe que, como você usa argumentos e a função de implantação é configurada para que você possa simplesmente desativar a opção de proxy e ter um contrato imutável em funcionamento, a opção methodName usa as opções args para argumento.
Isso significa que o construtor da implementação do proxy também precisa ter esses argumentos. Se você nunca pretende usar o contrato sem procuração, não pode fazer nada com os argumentos.

Na verdade, por conveniência, se o contrato de implementação tiver uma construção de argumento zero, ele ignorará o construtor inteiramente, mas não tenho certeza se foi uma boa ideia.

Aqui estaria o script com a opção 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'];

_Se você tornar sua função de inicialização idempotente, ou seja, ela pode ser chamada várias vezes sem lançar, certificando-se de que a chamada adicional não modifique o estado (simplesmente pulando se já estiver inicializada), então você pode usá-la através das opções methodName. a primeira implantação também a executará._

Isso significa que não podemos usar a função initialize() AS-IS de https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable/blob/release-v4.0/contracts/proxy/utils/Initializable.sol .

@freebyte mesmo

Vou considerar adicionar uma opção para obter esse tipo de comportamento

Ótimo, obrigado!

Outra questão é sobre o uso de contratos implantados. Particularmente, estou perguntando sobre Uniswap .

Nesta parte dos documentos do plug-in, não vejo como forneço o endereço do contrato Uniswap implantado. O Uniswap NPM vem com a pasta build com os artefatos mas como faço para que o plugin saiba sobre o contrato em um endereço específico? Não queremos usar éteres no script de implantação, certo?

O código a seguir NÃO é o que queremos no script de implantação:

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

Como faço para obter a implantação do Uniswap em deployments var?

para contratos já implantados, o melhor é criar arquivos para eles na pasta de implantações correspondente
então para mainnet uniswap e supondo que você tenha uma rede mainnet configurada em hardhat.config.js, você pode criar o arquivo "deployments/mainnet/UniswapFactory.json". No mínimo, ele precisa de um endereço e campo abi

então você pode acessá-lo por meio de implantações quando estiver nessa rede

O link doc que você aponta para permitir que você acesse artefatos (portanto, a pasta de build uniswap) ou script de deploy, mas o último só funciona se o projeto em questão expôs esses scripts de deploy em algum lugar

Eu faço isso em meu próprio projeto, como https://github.com/wighawag/universal-forwarder , que permite ao usuário simplesmente instalar o pacote npm e obter todo o projeto implantado em seu teste ou em sua própria rede. Mas o uniswap não fornece isso

Excelente. Muito obrigado.

Reinicializar a função para proxies

Eu adicionei novas opções (em vez de methodName que ainda está disponível) no hardhat-deploy 0.8.0

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

Deixe-me saber se você encontrar algum problema

Esta página foi útil?
0 / 5 - 0 avaliações