Hardhat-deploy: Menggunakan Open Zeppelin Upgrade Plugin

Dibuat pada 14 Mar 2021  ·  2Komentar  ·  Sumber: wighawag/hardhat-deploy

Halo,

Saya menemukan bahwa pembuat paket ini telah mempertimbangkan untuk mengintegrasikan hardhat-deploy dengan plugin pemutakhiran zeppelin terbuka untuk hardhat tetapi juga ingin menegaskan bahwa penambahan ini akan bagus.

Saat ini kami menggunakan plugin oz upgrade dan sementara kami juga ingin menggunakan plugin hardhat-deploy, kami tidak ingin mengorbankan hal-hal yang ditawarkan plugin oz kepada kami.

enhancement

Komentar yang paling membantu

Halo @Remscar , pasti akan luar biasa.
Sementara itu, saya mengembangkan plugin "solusi" untuk melakukannya, saya menyalin beberapa kode dari perpustakaan @openzeppelin/upgrade-core.
Agak jelek tapi bisa berhasil (gunakan dengan risiko Anda sendiri).

Impor file ini ( setelah mengimpor hardhat-deploy ) di 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,
  }
})

Penggunaan :

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
    }
  )
}

Semua 2 komentar

Halo @Remscar , pasti akan luar biasa.
Sementara itu, saya mengembangkan plugin "solusi" untuk melakukannya, saya menyalin beberapa kode dari perpustakaan @openzeppelin/upgrade-core.
Agak jelek tapi bisa berhasil (gunakan dengan risiko Anda sendiri).

Impor file ini ( setelah mengimpor hardhat-deploy ) di 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,
  }
})

Penggunaan :

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
    }
  )
}

Sekarang ada cara bawaan untuk menggunakan Openzeppelin Transparent Proxy. Saya akan menutup masalah ini

Apakah halaman ini membantu?
0 / 5 - 0 peringkat

Masalah terkait

jsidorenko picture jsidorenko  ·  3Komentar

lcswillems picture lcswillems  ·  14Komentar

freebyte picture freebyte  ·  9Komentar

gitpusha picture gitpusha  ·  6Komentar

jaypaik picture jaypaik  ·  13Komentar