Hardhat-deploy: рдУрдкрди рдЬрд╝реЗрдкреЗрд▓рд┐рди рдЕрдкрдЧреНрд░реЗрдб рдкреНрд▓рдЧрдЗрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛

рдХреЛ рдирд┐рд░реНрдорд┐рдд 14 рдорд╛рд░реНрдЪ 2021  ┬╖  2рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ  ┬╖  рд╕реНрд░реЛрдд: wighawag/hardhat-deploy

рдирдорд╕реНрддреЗ,

рдореИрдВрдиреЗ рдкрд╛рдпрд╛ рдХрд┐ рдЗрд╕ рдкреИрдХреЗрдЬ рдХреЗ рдирд┐рд░реНрдорд╛рддрд╛ рдиреЗ рдкрд╣рд▓реЗ рд╣реА рд╣рд╛рд░реНрдбрд╣реИрдЯ рдХреЗ рд▓рд┐рдП рдЦреБрд▓реЗ рдЬреЗрдкреЗрд▓рд┐рди рдЕрдкрдЧреНрд░реЗрдб рдкреНрд▓рдЧрдЗрди рдХреЗ рд╕рд╛рде hardhat-deploy рдХреЛ рдПрдХреАрдХреГрдд рдХрд░рдиреЗ рдкрд░ рдзреНрдпрд╛рди рджрд┐рдпрд╛ рд╣реИ, рд▓реЗрдХрд┐рди рдпрд╣ рднреА рдкреНрд░рддрд┐рдзреНрд╡рдирд┐рдд рдХрд░рдирд╛ рдЪрд╛рд╣реЗрдВрдЧреЗ рдХрд┐ рдпрд╣ рдЕрддрд┐рд░рд┐рдХреНрдд рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд╣реЛрдЧрд╛ред

рд╡рд░реНрддрдорд╛рди рдореЗрдВ рд╣рдо oz рдЕрдкрдЧреНрд░реЗрдб рдкреНрд▓рдЧрдЗрди рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░ рд░рд╣реЗ рд╣реИрдВ рдФрд░ рдЬрдмрдХрд┐ рд╣рдо рд╣рд╛рд░реНрдбрд╣реИрдЯ-рдбрд┐рдкреНрд▓реЙрдп рдкреНрд▓рдЧрдЗрди рдХрд╛ рднреА рдЙрдкрдпреЛрдЧ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ, рд╣рдо рдЙрди рдЪреАрдЬреЛрдВ рдХрд╛ рддреНрдпрд╛рдЧ рдирд╣реАрдВ рдХрд░рдирд╛ рдЪрд╛рд╣рддреЗ рд╣реИрдВ рдЬреЛ oz рдкреНрд▓рдЧрдЗрди рд╣рдореЗрдВ рдкреНрд░рджрд╛рди рдХрд░рддрд╛ рд╣реИред

рд╕рдмрд╕реЗ рдЙрдкрдпреЛрдЧреА рдЯрд┐рдкреНрдкрдгреА

рд╣реИрд▓реЛ @Remscar , рдпрд╣ рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд╣реЛрдЧрд╛ред
рдЗрд╕ рдмреАрдЪ, рдореИрдВрдиреЗ рдЗрд╕реЗ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП "рд╡рд░реНрдХрдЕрд░рд╛рдЙрдВрдб" рдкреНрд▓рдЧрдЗрди рд╡рд┐рдХрд╕рд┐рдд рдХрд┐рдпрд╛, рдореИрдВрдиреЗ @ openzeppelin/upgrad-core рд▓рд╛рдЗрдмреНрд░реЗрд░реА рд╕реЗ рдХреБрдЫ рдХреЛрдб рдХреЙрдкреА рдХрд┐рдПред
рдпрд╣ рдереЛрдбрд╝рд╛ рдмрджрд╕реВрд░рдд рд╣реИ рд▓реЗрдХрд┐рди рдпрд╣ рдХрд╛рдо рдХрд░ рд╕рдХрддрд╛ рд╣реИ (рдЗрд╕реЗ рдЕрдкрдиреЗ рдЬреЛрдЦрд┐рдо рдкрд░ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░реЗрдВ)ред

рд╣рд╛рд░реНрдбрд╣реИрдЯ-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,
  }
})

рдЙрдкрдпреЛрдЧ:

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

рд╕рднреА 2 рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

рд╣реИрд▓реЛ @Remscar , рдпрд╣ рдирд┐рд╢реНрдЪрд┐рдд рд░реВрдк рд╕реЗ рдмрд╣реБрдд рдЕрдЪреНрдЫрд╛ рд╣реЛрдЧрд╛ред
рдЗрд╕ рдмреАрдЪ, рдореИрдВрдиреЗ рдЗрд╕реЗ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП "рд╡рд░реНрдХрдЕрд░рд╛рдЙрдВрдб" рдкреНрд▓рдЧрдЗрди рд╡рд┐рдХрд╕рд┐рдд рдХрд┐рдпрд╛, рдореИрдВрдиреЗ @ openzeppelin/upgrad-core рд▓рд╛рдЗрдмреНрд░реЗрд░реА рд╕реЗ рдХреБрдЫ рдХреЛрдб рдХреЙрдкреА рдХрд┐рдПред
рдпрд╣ рдереЛрдбрд╝рд╛ рдмрджрд╕реВрд░рдд рд╣реИ рд▓реЗрдХрд┐рди рдпрд╣ рдХрд╛рдо рдХрд░ рд╕рдХрддрд╛ рд╣реИ (рдЗрд╕реЗ рдЕрдкрдиреЗ рдЬреЛрдЦрд┐рдо рдкрд░ рдЗрд╕реНрддреЗрдорд╛рд▓ рдХрд░реЗрдВ)ред

рд╣рд╛рд░реНрдбрд╣реИрдЯ-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,
  }
})

рдЙрдкрдпреЛрдЧ:

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

Openzeppelin рдкрд╛рд░рджрд░реНрд╢реА рдкреНрд░реЙрдХреНрд╕реА рдХрд╛ рдЙрдкрдпреЛрдЧ рдХрд░рдиреЗ рдХреЗ рд▓рд┐рдП рдЕрдм рдПрдХ рдЕрдВрддрд░реНрдирд┐рд╣рд┐рдд рддрд░реАрдХрд╛ рд╣реИред рдореИрдВ рдЗрд╕ рдореБрджреНрджреЗ рдХреЛ рдмрдВрдж рдХрд░ рджреВрдВрдЧрд╛

рдХреНрдпрд╛ рдпрд╣ рдкреГрд╖реНрда рдЙрдкрдпреЛрдЧреА рдерд╛?
0 / 5 - 0 рд░реЗрдЯрд┐рдВрдЧреНрд╕

рд╕рдВрдмрдВрдзрд┐рдд рдореБрджреНрджреЛрдВ

gitpusha picture gitpusha  ┬╖  6рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

gitpusha picture gitpusha  ┬╖  10рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

lcswillems picture lcswillems  ┬╖  14рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

lepidotteri picture lepidotteri  ┬╖  5рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ

jsidorenko picture jsidorenko  ┬╖  3рдЯрд┐рдкреНрдкрдгрд┐рдпрд╛рдБ