Hardhat-deploy: Utilisation du plug-in de mise à niveau Open Zeppelin

Créé le 14 mars 2021  ·  2Commentaires  ·  Source: wighawag/hardhat-deploy

Bonjour,

J'ai découvert que le créateur de ce package avait déjà envisagé d'intégrer hardhat-deploy avec le plug-in de mise à niveau open zeppelin pour casque, mais aimerait également faire écho au fait que cet ajout serait formidable.

Actuellement, nous utilisons le plugin oz upgrades et bien que nous aimerions également utiliser le plugin hardhat-deploy, nous ne voulons pas sacrifier les choses que le plugin oz nous offre.

enhancement

Commentaire le plus utile

Bonjour @Remscar , ce serait vraiment génial.
Pendant ce temps, j'ai développé un plugin "de contournement" pour le faire, j'ai copié du code de la bibliothèque @openzeppelin/upgrade-core.
C'est un peu moche mais ça peut faire le travail (utilisez-le à vos risques et périls).

Importez ce fichier ( après avoir importé hardhat-deploy ) dans le hardhat-config.js :

const { extendEnvironment } = require("hardhat/config")
const AdminUpgradeabilityProxy = require("@openzeppelin/upgrades-core/artifacts/contracts/proxy/AdminUpgradeabilityProxy.sol/AdminUpgradeabilityProxy.json")
const ProxyAdmin = require("@openzeppelin/upgrades-core/artifacts/contracts/proxy/ProxyAdmin.sol/ProxyAdmin.json")
const { assertUpgradeSafe, getVersion, getUnlinkedBytecode } = require("@openzeppelin/upgrades-core")
const { readValidations } = require("@openzeppelin/hardhat-upgrades/dist/validations")

const getSigner = async address => {
  const signers = await hre.ethers.getSigners()
  return signers.find(signer => signer.address === address)
}

const getInitializerData = (ImplFactory, args = [], initializer) => {
  if (initializer === false) {
    return "0x"
  }
  const allowNoInitialization = initializer === undefined && args.length === 0
  initializer = initializer ?? "initialize"
  try {
    const fragment = ImplFactory.interface.getFunction(initializer)
    return ImplFactory.interface.encodeFunctionData(fragment, args)
  } catch (e) {
    if (e instanceof Error) {
      if (allowNoInitialization && e.message.includes("no matching function")) {
        return "0x"
      }
    }
    throw e
  }
}

const deployProxyAdmin = async (owner) => {
  const { deployments } = hre
  const { deploy } = deployments
  return await deploy("ProxyAdmin", {
    contract: ProxyAdmin,
    from: owner,
    log: true,
  })
}

const deployOrUpgrade = async (firstImplName, opts, { initializer, postUpgrade, upgrades }) => {
  const { deployments } = hre
  const { deploy } = deployments
  let proxyAdmin
  try {
    proxyAdmin = await deployments.get("ProxyAdmin")
  } catch (error) {
    proxyAdmin = await deployProxyAdmin(opts.from)
  }
  const proxyName = `${firstImplName}Proxy`
  const firstImpl = await deploy(firstImplName, opts)
  const initData = getInitializerData(
    await ethers.getContractFactory(firstImplName),
    initializer && initializer.args ? initializer.args : [],
    initializer ? initializer.method : false
  )
  const proxy = await deploy(proxyName, {
    contract: AdminUpgradeabilityProxy,
    from: opts.from,
    log: true,
    args: [firstImpl.address, proxyAdmin.address, initData],
  })

  if (upgrades && upgrades.length > 0) {
    let previousImplName, newImplName
    if (upgrades.length === 1) {
      previousImplName = firstImplName
      newImplName = upgrades[0]
    } else {
      newImplName = upgrades.pop()
      previousImplName = upgrades.pop()
      for (oldUpgrade in upgrades) {
        // unsure previous upgrades exists
        await deployments.get(upgrades[oldUpgrade])
      }
    }
    if (previousImplName === newImplName) throw new Error("Same implementation, can't upgrade.")
    const newImplFactory = await ethers.getContractFactory(newImplName)
    const validations = await readValidations(hre)
    const unlinkedBytecode = getUnlinkedBytecode(validations, newImplFactory.bytecode)
    const version = getVersion(unlinkedBytecode, newImplFactory.bytecode)
    assertUpgradeSafe(validations, version, {
      unsafeAllowCustomTypes: false,
      unsafeAllowLinkedLibraries: false,
    })

    const signer = await getSigner(opts.from)
    const proxyAdminContract = await ethers.getContractAt(proxyAdmin.abi, proxyAdmin.address, signer)
    const previousImpl = await deployments.get(previousImplName)
    const actualImpl = await proxyAdminContract.getProxyImplementation(proxy.address)
    const newImpl = await deploy(newImplName, {
      from: opts.from,
      log: true,
    })
    if (newImpl.newlyDeployed)
      if (actualImpl == previousImpl.address) {
        console.log(`Upgrading from ${previousImplName} to ${newImplName}`)
        if (postUpgrade && postUpgrade.method && postUpgrade.args) {
          const upgradeData = getInitializerData(await ethers.getContractFactory(newImplName), postUpgrade.args, postUpgrade.method)
          await proxyAdminContract.upgradeAndCall(proxy.address, newImpl.address, upgradeData)
        } else await proxyAdminContract.upgrade(proxy.address, newImpl.address)
      } else throw new Error(`Proxy is actually pointing on: ${actualImpl}`)
  }
  return proxy
}

extendEnvironment(hre => {
  hre.myPlugin = {
    deployOrUpgrade,
  }
})

Utilisation :

module.exports = async ({ getNamedAccounts, myPlugin }) => {
  const { deployer } = await getNamedAccounts()
  // This will deploy the "Contract" with a proxy and  a proxyAdmin if it doesn't exist.
  const proxy = await myPlugin.deployOrUpgrade(
    "Contract",
    { from: deployer, log: true },
    {
      initializer: { method: "initialize", args: [] },
      // bellow attributes for upgrading
      postUpgrade: { method: "postUpgrade", args: [] }, // method to  exec after upgrade
      upgrades: ["ContractV2", "ContractV3"], // you should keep the list of upgrades and add the new one at  the last
    }
  )
}

Tous les 2 commentaires

Bonjour @Remscar , ce serait vraiment génial.
Pendant ce temps, j'ai développé un plugin "de contournement" pour le faire, j'ai copié du code de la bibliothèque @openzeppelin/upgrade-core.
C'est un peu moche mais ça peut faire le travail (utilisez-le à vos risques et périls).

Importez ce fichier ( après avoir importé hardhat-deploy ) dans le hardhat-config.js :

const { extendEnvironment } = require("hardhat/config")
const AdminUpgradeabilityProxy = require("@openzeppelin/upgrades-core/artifacts/contracts/proxy/AdminUpgradeabilityProxy.sol/AdminUpgradeabilityProxy.json")
const ProxyAdmin = require("@openzeppelin/upgrades-core/artifacts/contracts/proxy/ProxyAdmin.sol/ProxyAdmin.json")
const { assertUpgradeSafe, getVersion, getUnlinkedBytecode } = require("@openzeppelin/upgrades-core")
const { readValidations } = require("@openzeppelin/hardhat-upgrades/dist/validations")

const getSigner = async address => {
  const signers = await hre.ethers.getSigners()
  return signers.find(signer => signer.address === address)
}

const getInitializerData = (ImplFactory, args = [], initializer) => {
  if (initializer === false) {
    return "0x"
  }
  const allowNoInitialization = initializer === undefined && args.length === 0
  initializer = initializer ?? "initialize"
  try {
    const fragment = ImplFactory.interface.getFunction(initializer)
    return ImplFactory.interface.encodeFunctionData(fragment, args)
  } catch (e) {
    if (e instanceof Error) {
      if (allowNoInitialization && e.message.includes("no matching function")) {
        return "0x"
      }
    }
    throw e
  }
}

const deployProxyAdmin = async (owner) => {
  const { deployments } = hre
  const { deploy } = deployments
  return await deploy("ProxyAdmin", {
    contract: ProxyAdmin,
    from: owner,
    log: true,
  })
}

const deployOrUpgrade = async (firstImplName, opts, { initializer, postUpgrade, upgrades }) => {
  const { deployments } = hre
  const { deploy } = deployments
  let proxyAdmin
  try {
    proxyAdmin = await deployments.get("ProxyAdmin")
  } catch (error) {
    proxyAdmin = await deployProxyAdmin(opts.from)
  }
  const proxyName = `${firstImplName}Proxy`
  const firstImpl = await deploy(firstImplName, opts)
  const initData = getInitializerData(
    await ethers.getContractFactory(firstImplName),
    initializer && initializer.args ? initializer.args : [],
    initializer ? initializer.method : false
  )
  const proxy = await deploy(proxyName, {
    contract: AdminUpgradeabilityProxy,
    from: opts.from,
    log: true,
    args: [firstImpl.address, proxyAdmin.address, initData],
  })

  if (upgrades && upgrades.length > 0) {
    let previousImplName, newImplName
    if (upgrades.length === 1) {
      previousImplName = firstImplName
      newImplName = upgrades[0]
    } else {
      newImplName = upgrades.pop()
      previousImplName = upgrades.pop()
      for (oldUpgrade in upgrades) {
        // unsure previous upgrades exists
        await deployments.get(upgrades[oldUpgrade])
      }
    }
    if (previousImplName === newImplName) throw new Error("Same implementation, can't upgrade.")
    const newImplFactory = await ethers.getContractFactory(newImplName)
    const validations = await readValidations(hre)
    const unlinkedBytecode = getUnlinkedBytecode(validations, newImplFactory.bytecode)
    const version = getVersion(unlinkedBytecode, newImplFactory.bytecode)
    assertUpgradeSafe(validations, version, {
      unsafeAllowCustomTypes: false,
      unsafeAllowLinkedLibraries: false,
    })

    const signer = await getSigner(opts.from)
    const proxyAdminContract = await ethers.getContractAt(proxyAdmin.abi, proxyAdmin.address, signer)
    const previousImpl = await deployments.get(previousImplName)
    const actualImpl = await proxyAdminContract.getProxyImplementation(proxy.address)
    const newImpl = await deploy(newImplName, {
      from: opts.from,
      log: true,
    })
    if (newImpl.newlyDeployed)
      if (actualImpl == previousImpl.address) {
        console.log(`Upgrading from ${previousImplName} to ${newImplName}`)
        if (postUpgrade && postUpgrade.method && postUpgrade.args) {
          const upgradeData = getInitializerData(await ethers.getContractFactory(newImplName), postUpgrade.args, postUpgrade.method)
          await proxyAdminContract.upgradeAndCall(proxy.address, newImpl.address, upgradeData)
        } else await proxyAdminContract.upgrade(proxy.address, newImpl.address)
      } else throw new Error(`Proxy is actually pointing on: ${actualImpl}`)
  }
  return proxy
}

extendEnvironment(hre => {
  hre.myPlugin = {
    deployOrUpgrade,
  }
})

Utilisation :

module.exports = async ({ getNamedAccounts, myPlugin }) => {
  const { deployer } = await getNamedAccounts()
  // This will deploy the "Contract" with a proxy and  a proxyAdmin if it doesn't exist.
  const proxy = await myPlugin.deployOrUpgrade(
    "Contract",
    { from: deployer, log: true },
    {
      initializer: { method: "initialize", args: [] },
      // bellow attributes for upgrading
      postUpgrade: { method: "postUpgrade", args: [] }, // method to  exec after upgrade
      upgrades: ["ContractV2", "ContractV3"], // you should keep the list of upgrades and add the new one at  the last
    }
  )
}

Il existe maintenant un moyen intégré d'utiliser Openzeppelin Transparent Proxy. je ferme ce sujet

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