์๋ ํ์ธ์,
์ด ํจํค์ง์ ์์ฑ์๊ฐ ์ด๋ฏธ hardhat-deploy
์ hardhat์ฉ ๊ฐ๋ฐฉํ zeppelin ์
๊ทธ๋ ์ด๋ ํ๋ฌ๊ทธ์ธ๊ณผ ํตํฉํ๋ ๊ฒ์ ๊ฒํ ํ์ง๋ง ์ด ์ถ๊ฐ๊ฐ ํ๋ฅญํ ๊ฒ์ด๋ผ๋ ์ ์ ๊ฐ์กฐํ๊ณ ์ถ์ต๋๋ค.
ํ์ฌ ์ฐ๋ฆฌ๋ oz ์ ๊ทธ๋ ์ด๋ ํ๋ฌ๊ทธ์ธ์ ์ฌ์ฉํ๊ณ ์์ผ๋ฉฐ hardhat-deploy ํ๋ฌ๊ทธ์ธ๋ ์ฌ์ฉํ๊ณ ์ถ์ง๋ง oz ํ๋ฌ๊ทธ์ธ์ด ์ ๊ณตํ๋ ๊ฒ์ ํฌ์ํ๊ณ ์ถ์ง๋ ์์ต๋๋ค.
์๋
ํ์ธ์ @Remscar๋ , ํ์คํ ์ข์ ๊ฒ์
๋๋ค.
๊ทธ ์ฌ์ด์ "ํด๊ฒฐ์ฑ
" ํ๋ฌ๊ทธ์ธ์ ๊ฐ๋ฐํ์ฌ @openzeppelin/upgrade-core ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ผ๋ถ ์ฝ๋๋ฅผ ๋ณต์ฌํ์ต๋๋ค.
์ฝ๊ฐ ๋ชป์๊ฒผ์ง๋ง ์์
์ ์ํํ ์ ์์ต๋๋ค(์์ ์ ์ฑ
์ํ์ ์ฌ์ฉ).
hardhat-config.js์์ ์ด ํ์ผ์ ๊ฐ์ ธ์ต๋๋ค( hardhat-deploy๋ฅผ ๊ฐ์ ธ์จ ํ).
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 ํฌ๋ช ํ๋ก์๋ฅผ ์ฌ์ฉํ๋ ๊ธฐ๋ณธ ์ ๊ณต ๋ฐฉ๋ฒ์ด ์์ต๋๋ค. ์ด ๋ฌธ์ ๋ฅผ ๋ซ๊ฒ ์ต๋๋ค
๊ฐ์ฅ ์ ์ฉํ ๋๊ธ
์๋ ํ์ธ์ @Remscar๋ , ํ์คํ ์ข์ ๊ฒ์ ๋๋ค.
๊ทธ ์ฌ์ด์ "ํด๊ฒฐ์ฑ " ํ๋ฌ๊ทธ์ธ์ ๊ฐ๋ฐํ์ฌ @openzeppelin/upgrade-core ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ผ๋ถ ์ฝ๋๋ฅผ ๋ณต์ฌํ์ต๋๋ค.
์ฝ๊ฐ ๋ชป์๊ฒผ์ง๋ง ์์ ์ ์ํํ ์ ์์ต๋๋ค(์์ ์ ์ฑ ์ํ์ ์ฌ์ฉ).
hardhat-config.js์์ ์ด ํ์ผ์ ๊ฐ์ ธ์ต๋๋ค( hardhat-deploy๋ฅผ ๊ฐ์ ธ์จ ํ).
์ฉ๋ฒ :