Hardhat-deploy: ИспользованиС плагина обновлСния Open Zeppelin

Π‘ΠΎΠ·Π΄Π°Π½Π½Ρ‹ΠΉ Π½Π° 14 ΠΌΠ°Ρ€. 2021  Β·  2ΠšΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΈ  Β·  Π˜ΡΡ‚ΠΎΡ‡Π½ΠΈΠΊ: wighawag/hardhat-deploy

ΠŸΡ€ΠΈΠ²Π΅Ρ‚,

Π― ΠΎΠ±Π½Π°Ρ€ΡƒΠΆΠΈΠ», Ρ‡Ρ‚ΠΎ ΡΠΎΠ·Π΄Π°Ρ‚Π΅Π»ΡŒ этого ΠΏΠ°ΠΊΠ΅Ρ‚Π° ΡƒΠΆΠ΅ рассматривал Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΡΡ‚ΡŒ ΠΈΠ½Ρ‚Π΅Π³Ρ€Π°Ρ†ΠΈΠΈ hardhat-deploy с ΠΏΠ»Π°Π³ΠΈΠ½ΠΎΠΌ open zeppelin upgrades для каски, Π½ΠΎ Ρ‚Π°ΠΊΠΆΠ΅ Ρ…ΠΎΡ‚Π΅Π» Π±Ρ‹ ΠΏΠΎΠ²Ρ‚ΠΎΡ€ΠΈΡ‚ΡŒ, Ρ‡Ρ‚ΠΎ это Π΄ΠΎΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ Π±Ρ‹Π»ΠΎ Π±Ρ‹ ΠΎΡ‚Π»ΠΈΡ‡Π½Ρ‹ΠΌ.

Π’ настоящСС врСмя ΠΌΡ‹ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ ΠΏΠ»Π°Π³ΠΈΠ½ oz upgrades, ΠΈ хотя ΠΌΡ‹ Ρ…ΠΎΡ‚Π΅Π»ΠΈ Π±Ρ‹ Ρ‚Π°ΠΊΠΆΠ΅ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΠΎΠ²Π°Ρ‚ΡŒ ΠΏΠ»Π°Π³ΠΈΠ½ hardhat-deploy, ΠΌΡ‹ Π½Π΅ Ρ…ΠΎΡ‚ΠΈΠΌ ΠΆΠ΅Ρ€Ρ‚Π²ΠΎΠ²Π°Ρ‚ΡŒ Ρ‚Π΅ΠΌ, Ρ‡Ρ‚ΠΎ ΠΏΡ€Π΅Π΄Π»Π°Π³Π°Π΅Ρ‚ Π½Π°ΠΌ ΠΏΠ»Π°Π³ΠΈΠ½ oz.

Π‘Π°ΠΌΡ‹ΠΉ ΠΏΠΎΠ»Π΅Π·Π½Ρ‹ΠΉ ΠΊΠΎΠΌΠΌΠ΅Π½Ρ‚Π°Ρ€ΠΈΠΉ

ΠŸΡ€ΠΈΠ²Π΅Ρ‚ @Remscar , это ΠΎΠΏΡ€Π΅Π΄Π΅Π»Π΅Π½Π½ΠΎ Π±Ρ‹Π»ΠΎ Π±Ρ‹ Π·Π΄ΠΎΡ€ΠΎΠ²ΠΎ.
Π’Π΅ΠΌ Π²Ρ€Π΅ΠΌΠ΅Π½Π΅ΠΌ я Ρ€Π°Π·Ρ€Π°Π±ΠΎΡ‚Π°Π» для этого ΠΎΠ±Ρ…ΠΎΠ΄Π½ΠΎΠΉ ΠΏΠ»Π°Π³ΠΈΠ½, скопировав Π½Π΅ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ ΠΊΠΎΠ΄ ΠΈΠ· Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠΈ @openzeppelin/upgrade-core.
Π­Ρ‚ΠΎ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ ΡƒΡ€ΠΎΠ΄Π»ΠΈΠ²ΠΎ, Π½ΠΎ ΠΎΠ½ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ (ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ Π΅Π³ΠΎ Π½Π° свой страх ΠΈ риск).

Π˜ΠΌΠΏΠΎΡ€Ρ‚ΠΈΡ€ΡƒΠΉΡ‚Π΅ этот Ρ„Π°ΠΉΠ» (послС ΠΈΠΌΠΏΠΎΡ€Ρ‚Π° hardhat-deploy) Π² 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,
  }
})

ΠŸΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ :

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/upgrade-core.
Π­Ρ‚ΠΎ Π½Π΅ΠΌΠ½ΠΎΠ³ΠΎ ΡƒΡ€ΠΎΠ΄Π»ΠΈΠ²ΠΎ, Π½ΠΎ ΠΎΠ½ΠΎ ΠΌΠΎΠΆΠ΅Ρ‚ Ρ€Π°Π±ΠΎΡ‚Π°Ρ‚ΡŒ (ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠΉΡ‚Π΅ Π΅Π³ΠΎ Π½Π° свой страх ΠΈ риск).

Π˜ΠΌΠΏΠΎΡ€Ρ‚ΠΈΡ€ΡƒΠΉΡ‚Π΅ этот Ρ„Π°ΠΉΠ» (послС ΠΈΠΌΠΏΠΎΡ€Ρ‚Π° hardhat-deploy) Π² 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,
  }
})

ΠŸΡ€ΠΈΠΌΠ΅Π½Π΅Π½ΠΈΠ΅ :

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 Transparent Proxy. я Π·Π°ΠΊΡ€ΠΎΡŽ этот вопрос

Π‘Ρ‹Π»Π° Π»ΠΈ эта страница ΠΏΠΎΠ»Π΅Π·Π½ΠΎΠΉ?
0 / 5 - 0 Ρ€Π΅ΠΉΡ‚ΠΈΠ½Π³ΠΈ