Skip to content

choyeah/UpgradableLearningKit

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

1 Commit
ย 
ย 
ย 
ย 

Repository files navigation

Upgradable Contract ๋ž€?

์—…๊ทธ๋ ˆ์ด๋”๋ธ”(Upgradable) ์ปจํŠธ๋ž™ํŠธ๋Š” ํ”„๋ก์‹œ ํŒจํ„ด์— ๊ธฐ๋ฐ˜ํ•œ๋‹ค. ํ”„๋ก์‹œ ํŒจํ„ด์ด๋ž€ ํด๋ผ์ด์–ธํŠธ์™€ ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ ์ค‘๊ฐ„ ๊ณ„์ธต์œผ๋กœ, ํด๋ผ์ด์–ธํŠธ๊ฐ€ ์Šค๋งˆํŠธ ์ปจํŠธ๋ž™ํŠธ์— ์ง์ ‘ ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ํ†ตํ•ด ๊ฐ„์ ‘์ ์œผ๋กœ ์ ‘๊ทผํ•˜๋„๋ก ํ•œ๋А ํŒจํ„ด์ด๋‹ค. ์ด๋Ÿฐ ํ”„๋ก์‹œ ํŒจํ„ด์€ fallback() ํ•จ์ˆ˜์™€ delegate_call() ํ•จ์ˆ˜, ์ด ๋‘ ๊ฐ€์ง€ ํ•จ์ˆ˜์˜ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ํ•จ๊ป˜ ์‚ฌ์šฉ๋˜์–ด ๊ตฌํ˜„๋œ๋‹ค.

ํ”„๋ก์‹œ ํŒจํ„ด ์ฐธ๊ณ : https://blog.openzeppelin.com/proxy-patterns/

fallback() ํ•จ์ˆ˜์˜ ์—ญํ• 

Solidity์—์„œ fallback ํ•จ์ˆ˜๊ฐ€ ํ˜ธ์ถœ๋˜๋Š” ์กฐ๊ฑด์€ ๋‹ค์Œ๊ณผ ๊ฐ™๋‹ค.

  • EOA ๋˜๋Š” CA์—์„œ ํŠน์ • ์ปจํŠธ๋ž™ํŠธ๋กœ ์ด๋”๋ฅผ ์ „์†กํ•˜๋Š” ๊ฒฝ์šฐ
  • ์ปจํŠธ๋ž™ํŠธ์— ์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•  ๋•Œ
  • ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ๋‹ค๋ฅธ ์ปจํŠธ๋ž™ํŠธ๋กœ๋ถ€ํ„ฐ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ์ฝœ์„ ๋ฐ›๋Š” ๊ฒฝ์šฐ

์ตœ์ดˆ์˜ ํ˜ธ์ถœํ•˜๋Š” ๊ณ„์ •์€ ์ค‘๊ณ„ ์—ญํ• ์„ํ•˜๋Š” ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ์— ์กด์žฌํ•˜์ง€ ์•Š๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ fallback() ํ•จ์ˆ˜๋ฅผ ๋ฐœ๋™์‹œํ‚ค๊ฒŒ๋˜๊ณ  ์ด fallback() ํ•จ์ˆ˜ ๋‚ด๋ถ€์—์„œ๋Š” delegate_call()์„ ์‚ฌ์šฉํ•˜์—ฌ ์ตœ์ดˆ ํ˜ธ์ถœ ๊ณ„์ •์œผ๋กœ๋ถ€ํ„ฐ ์ „๋‹ฌ๋œ ํŽ‘์…˜์‹œ๊ทธ์™€ ๋ฐ์ดํ„ฐ๋ฅผ ๋กœ์ง์„ ๋‹ด๋‹นํ•˜๋Š” ์ž„ํ”Œ๋ฆฌ๋ฉ˜ํŠธ ์ปจํŠธ๋ž™ํŠธ์— ์ „๋‹ฌํ•œ๋‹ค.

delegate_call()์˜ ์—ญํ• 

https://twitter.com/definikola/status/1512100191804997640

๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ์ฝœ์€ ํ˜ธ์ถœ๋œ ์ปจํŠธ๋ž™ํŠธ์˜ ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœ์ž์˜ ์ปจํ…์ŠคํŠธ์—์„œ ์‹คํ–‰ํ•˜๋Š” ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด๋‹ค. ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ํ†ตํ•ด ์ž„ํ”Œ๋ฆฌ๋ฉ˜ํŠธ(๋กœ์ง) ์ปจํŠธ๋ž™ํŠธ์˜ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•  ๋•Œ, ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ์˜ ์ปจํ…์ŠคํŠธ์—์„œ ์ฝ”๋“œ๊ฐ€ ์‹คํ–‰๋˜๋ฏ€๋กœ ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ์˜ ์ƒํƒœ๊ฐ€ ๋ณ€๊ฒฝ๋œ๋‹ค. ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ์— ๋ฐ์ดํ„ฐ๊ฐ€ ์ €์žฅ๋˜๋Š” ์›๋ฆฌ์ด๋‹ค.

Proxy ์ปจํŠธ๋ž™ํŠธ ์‚ฌ์šฉ ์˜ˆ์‹œ

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@openzeppelin/contracts/proxy/Proxy.sol";

contract ERCProxy is Proxy {

    address public implementation;

    constructor(address _impleAddress, CampaignParams memory _campaignParams) {
        implementation = _impleAddress;
    }

    function _implementation() internal view override returns (address) {
        return implementation;
    }
}

  1. ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์ƒ์†๋ฐ›์€ ํ›„
  2. ์ƒ์„ฑ์ž์—์„œ ์ž„ํ”Œ๋ฆฌ๋ฉ˜ํŠธ ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ์ €์žฅํ•œ๋‹ค.
  3. ๋‹จ์ˆœํžˆ ์ž„ํ”Œ ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ๋ฅผ ๋ฆฌํ„ดํ•˜๋Š” _implementation()๋ฅผ ๊ตฌํ˜„ํ•ด์ค€๋‹ค.
  4. ์ด์ œ Proxy ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์ƒ์†๋ฐ›์€ ERCProxy ์ปจํŠธ๋ž™ํŠธ์— ์ž„ํ”Œ ์ปจํŠธ๋ž™ํŠธ์— ์กด์žฌํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ๋ฐ์ดํ„ฐ(์ธ์ž๋“ค)์™€ ํ•จ๊ป˜ ํ˜ธ์ถœํ•ด์ฃผ๋ฉด Proxy ์ปจํŠธ๋ž™ํŠธ์˜ fallback() ํ•จ์ˆ˜๋ฅผ ํƒ€๊ณ  ์ž„ํ”Œ ์ปจํŠธ๋ž™ํŠธ์˜ ํŠน์ • ํ•จ์ˆ˜๋ฅผ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ์ฝœ ํ•˜๊ฒŒ๋œ๋‹ค.
  5. ๋งŒ์•ฝ ์ž„ํ”Œ ์ปจํŠธ๋ž™ํŠธ์— ์ˆ˜์ • ์‚ฌํ•ญ์ด ์ƒ๊ธด๋‹ค๋ฉด ์ƒˆ๋กœ์šด ์ž„ํ”Œ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์žฌ ์ž‘์„ฑ/๋ฐฐํฌ ํ›„ implementation ๋ณ€์ˆ˜๋ฅผ ์ƒˆ๋กœ์šด ์ž„ํ”Œ ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ๋กœ ์—…๋ฐ์ดํŠธํ•œ๋‹ค.

OpenZeppelin Proxy contract ์ฝ”๋“œ

initialize()

ํ”„๋ก์‹œ ํŒจํ„ด์—์„œ๋Š” ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ์˜ ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์—†๋‹ค. ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ ๋ฐฐํฌ์‹œ์— ์ƒ์„ฑ์ž๊ฐ€ ๋”ฑ ํ•œ ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋˜๊ณ  ์ด ์ƒ์„ฑ์ž๋Š” ๋‹ค๋ฅธ ์ปจํŠธ๋ž™ํŠธ์— ์˜ํ•ด ํ˜ธ์ถœ๋  ์ˆ˜ ์—†๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.

๋กœ์ง ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ๋ฐฐํฌ๋˜๊ฑฐ๋‚˜ ์—…๊ทธ๋ ˆ์ด๋“œ๋œ ์ƒํ™ฉ์—์„œ ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ๋Š” ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ์— ์ƒํƒœ ๊ฐ’, ์˜ˆ๋ฅผ๋“ค์–ด ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ์˜ ์ฃผ์†Œ ์ €์žฅ๊ณผ ๊ฐ™์€ ์ƒํƒœ๋ณ€์ˆ˜ ์ดˆ๊ธฐํ™” ๋˜๋Š” ๋ณ€๊ฒฝ์ด ํ•„์š”ํ•œ๋ฐ ์ƒ์„ฑ์ž๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์œผ๋‹ˆ ์ƒ์„ฑ์ž์™€ ์œ ์‚ฌํ•œ ๋ผ์ดํ”„์‚ฌ์ดํด์—์„œ ๋‹จ ํ•œ๋ฒˆ๋งŒ ํ˜ธ์ถœ๋˜๋„๋ก ์„ค๊ณ„๋œ initialize() ํ•จ์ˆ˜๋ฅผ ๊ณ ์•ˆํ•˜๊ฒŒ ๋˜์—ˆ๋‹ค.

์ฐธ๊ณ ๋กœ ํ”„๋ก์‹œ์—์„œ ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ์˜ initialize() ํ•จ์ˆ˜๋ฅผ ํ†ตํ•ด ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ์˜ ์ฃผ์†Œ๋ฅผ ์ƒํƒœ ๋ณ€์ˆ˜์— ์ €์žฅํ•œ๋‹ค๋ฉด ๊ทธ๊ฒƒ์€ ๋ธ๋ฆฌ๊ฒŒ์ดํŠธ์ฝœ์— ์˜ํ•˜์—ฌ ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ์˜ ์ปจํ…์ŠคํŠธ์ด๊ธฐ ๋•Œ๋ฌธ์— ์ €์žฅ์€ ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ์— ์ €์žฅ๋œ๋‹ค.

openzeppelin upgrades ํ”Œ๋Ÿฌ๊ทธ์ธ

openzeppelin Upgrades ํ”Œ๋Ÿฌ๊ทธ์ธ์€ ์—…๊ทธ๋ ˆ์ด๋”๋ธ” ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์‰ฝ๊ฒŒ ๋ฐฐํฌ, ์—…๊ทธ๋ ˆ์ด๋“œ, ๊ด€๋ฆฌ์ž ๊ถŒํ•œ ๊ด€๋ฆฌ, ํ…Œ์ŠคํŠธํ•  ์ˆ˜ ์žˆ๋„๋ก ๋•๋Š”๋‹ค. ๊ฐœ๋ฐœ์ž๊ฐ€ ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ๋”ฐ๋กœ ์ž‘์„ฑํ•  ํ•„์š”๊ฐ€ ์—†๊ณ  ๋ฐฐํฌ์™€, ์—…๊ทธ๋ ˆ์ด๋“œ, ๊ด€๋ฆฌ์ž ๊ถŒํ•œ ๋ณ€๊ฒฝ ๊ธฐ๋Šฅ์„ ํ•จ์ˆ˜๋กœ ์ œ๊ณตํ•œ๋‹ค.

upgrades ํ”Œ๋Ÿฌ๊ทธ์ธ์€ UUPS, transparent, ๋น„์ฝ˜ ํ”„๋ก์‹œ ํŒจํ„ด์„ ์ง€์›ํ•œ๋‹ค.

  • UUPS: ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ์ž์‹ ์„ ์—…๊ทธ๋ ˆ์ด๋“œํ•  ์ˆ˜ ์žˆ๋Š” ๊ธฐ๋Šฅ์„ ๋‚ด์žฅํ•˜๊ณ  ์žˆ๋‹ค. ๊ฐ€์žฅ ํ”ํžˆ ์‚ฌ์šฉ๋˜๋ฉฐ ์˜คํ”ˆ์ œํ”Œ๋ฆฐ์—์„œ๋„ UUPS ํ”„๋ก์‹œ ํŒจํ„ด ์‚ฌ์šฉ์„ ๊ฐ€์žฅ ๊ถŒ์žฅํ•œ๋‹ค.
  • Transparent: ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ๋ฅผ ์—…๊ทธ๋ ˆ์ด๋“œํ•˜๋Š” ํ•จ์ˆ˜๋Š” ํ”„๋ก์‹œ, ๋กœ์ง ๋‘ ์ปจํŠธ๋ž™ํŠธ์— ์กด์žฌํ•˜๋‚˜ ์‚ฌ์šฉ์ž ์–ด์นด์šดํŠธ์™€ ์–ด๋“œ๋ฏผ ์–ด์นด์šดํŠธ์˜ ํ•จ์ˆ˜ ํ˜ธ์ถœ ๋Œ€์ƒ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ๋‹ค๋ฅด๊ฒŒ ํ•œ๋‹ค. (์–ด๋“œ๋ฏผ ๊ณ„์ •์€ ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ๋กœ, ์‚ฌ์šฉ์ž ๊ณ„์ •์€ ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ๋กœ) ์ด๋กœ์จ ํ•จ์ˆ˜ ์ถฉ๋Œ ์ด์Šˆ๋ฅผ ํ•ด์†Œํ•œ๋‹ค.
  • Beacon: ์—ฌ๋Ÿฌ ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ๊ฐ€ ํ•˜๋‚˜์˜ ๋น„์ฝ˜ ์ปจํŠธ๋ž™ํŠธ๋กœ๋ถ€ํ„ฐ ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ์˜ ์ฃผ์†Œ๋ฅผ ์–ป๋Š” ๋ฐฉ์‹์œผ๋กœ, ๋ชจ๋“  ํ”„๋ก์‹œ๋Š” ์ด ๋น„์ฝ˜ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ํ†ตํ•ด ์—…๊ทธ๋ ˆ์ด๋“œ๋œ ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ๋ฅผ ์–ป์–ด ์ ‘๊ทผ.

    ํ”„๋ก์‹œ ํŒจํ„ด ์ข…๋ฅ˜๋“ค ์ฐธ๊ณ 

UUPS ๋ฐ transparent ํ”„๋ก์‹œ์˜ ๊ฒฝ์šฐ deployProxy ๋ฐ upgradeProxy ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ๋น„์ฝ˜ ํ”„๋ก์‹œ์˜ ๊ฒฝ์šฐ deployBeacon, deployBeaconProxy ๋ฐ upgradeBeacon ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค.

openzeppelin upgrades ํ”Œ๋Ÿฌ๊ทธ์ธ์„ ์ด์šฉํ•œ ๋ฐฐํฌ & ์—…๊ทธ๋ ˆ์ด๋“œ

์ตœ์ดˆ ํ”„๋ก์‹œ & ์ž„ํ”Œ๋ฆฌ๋ฉ˜ํ…Œ์ด์…˜ ๋ฐฐํฌ ์Šคํฌ๋ฆฝํŠธ

import { ethers, upgrades } from "hardhat";

async function main() {
  const Box = await ethers.getContractFactory("Box");
  const proxyInstance = await upgrades.deployProxy(Box, [42], {initializer: 'store'});
  await proxyInstance.deployed();
  console.log("Box Proxy deployed to:", proxyInstance.address);
}

// We recommend this pattern to be able to use async/await everywhere
// and properly handle errors.
main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
npx hardhat run scripts/create-box.ts --network {}
Box Proxy deployed to: 0x0F20c143a98CdfB3a1487278Bcd310296c674498

์—…๊ทธ๋ ˆ์ด๋“œ ์Šคํฌ๋ฆฝํŠธ

import { ethers, upgrades } from "hardhat";

const proxyAddress = "0x8d33046c43808974d76C2874c8BbA8eDc06EF495";
async function upgrade() {
  const BoxV2 = await ethers.getContractFactory("BoxV2");

  console.log("Preparing upgrade...");
  // upgrades.prepareUpgrade() ํ•จ์ˆ˜๋Š” ์ƒˆ๋กœ์šด ์ž„ํ”Œ ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ๋ฅผ ๋ฐ˜ํ™˜
  const boxV2Address = await upgrades.prepareUpgrade(proxyAddress, BoxV2);
  console.log("BoxV2 Implemantaion address will be :", boxV2Address);
  const boxV2Proxy = await upgrades.upgradeProxy(proxyAddress, BoxV2);
  console.log("upgraded to same proxy address : ", boxV2Proxy.address);
}

upgrade();
npx hardhat run scripts/upgrade-box.ts --network {}
BoxV2 Implemantaion address will be : 0x4C18BB1a60fb0b9fF5747658cEC416CB91a9AE43
upgraded to same proxy address :  0x0F20c143a98CdfB3a1487278Bcd310296c674498

Impelmantion ์ปจํŠธ๋ ‰ํŠธ๋Š” ์ƒˆ๋กœ์šด v2 ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ๋กœ ๋ฐฐํฌ๋˜๊ณ , ์œ ์ €์™€ ์ƒํ˜ธ์ž‘์šฉํ•˜๋Š” ํ”„๋ก์‹œ ์ฃผ์†Œ๋Š” ๋™์ผํ•œ ์ฃผ์†Œ๋กœ์—…๊ทธ๋ ˆ์ด๋“œ ๋œ๋‹ค.

upgrades ํ”Œ๋Ÿฌ๊ทธ์ธ ํŠœํ† ๋ฆฌ์–ผ ๋ฌธ์„œ

ํ…Œ์ŠคํŠธ

TEST1 - tutorial_hardhat/test/Box.ts

  • ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ๋ฅผ ๋ฐฐํฌํ•˜๊ณ 
  • ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ์˜ ๊ธฐ๋Šฅ์„ ํ…Œ์ŠคํŠธ
import { expect } from 'chai';
import { Contract } from 'ethers';
import { ethers } from 'hardhat';
  // contracts
  let box: Contract;

  // Start test block
  describe('Box', function () {
    beforeEach(async function () {
      const Box = await ethers.getContractFactory("Box");
      box = await Box.deploy();
      await box.deployed();
      console.log("box deployed",box.address);
    });

    // Test case
    it('retrieve returns a value previously stored', async function () {
      // Store a value
      await box.store(42);

      // Test if the returned value is the same one
      // Note that we need to use strings to compare the 256 bit integers
      expect((await box.retrieve()).toString()).to.equal('42');
    });
  });

TEST2 - /tutorial_hardhat/test/Box.Proxy.ts

  • ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ๋ฅผ ๋ฐฐํฌ
  • ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ ๋ฐฐํฌ & ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ์™€ ๋ฐ”์ธ๋”ฉ (์ดˆ๊ธฐ ์‹คํ–‰ ํ•จ์ˆ˜ ์ง€์ •, ์ธ์ž ๊ฐ’ ์„ค์ •)
  • ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ ์ฃผ์†Œ๋กœ๋ถ€ํ„ฐ ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ์˜ ์ฃผ์†Œ ๊ตฌํ•˜๊ธฐ upgrades.erc1967.getImplementationAddress(boxProxy.address);
  • ํ”„๋ก์‹œ ์ปจํŠธ๋ž™ํŠธ๋ฅผ ํ†ตํ•ด ์ดˆ๊ธฐํ™” ์‹œ์ผฐ๋˜ ๋ฐ์ดํ„ฐ๊ฐ€ ์ •์ƒ ์ถœ๋ ฅ ๋˜๋Š”์ง€ ํ™•์ธ
import { expect } from "chai";
import { Contract } from "ethers";
import { ethers, upgrades } from "hardhat";
import { getImplementationAddress } from "@openzeppelin/upgrades-core";

// contracts
let boxProxy: Contract;

// Start test block
describe("Box (proxy)", function () {
  beforeEach(async function () {
    const Box = await ethers.getContractFactory("Box");
    boxProxy = await upgrades.deployProxy(Box, [42], { initializer: "store" });
    console.log("boxProxy deployProxy", boxProxy.address);
    const currentImplAddress = await upgrades.erc1967.getImplementationAddress(
      boxProxy.address
    );
    console.log("currentImplAddress", currentImplAddress);
  });

  // Test case
  it("retrieve returns a value previously initialized", async function () {
    // Test if the returned value is the same one
    // Note that we need to use strings to compare the 256 bit integers
    expect((await boxProxy.retrieve()).toString()).to.equal("42");
  });
});

TEST3 - /tutorial_hardhat/test/BoxV2.ts

  • ์ƒˆ๋กœ์šด ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ(BoxV2) ๋ฐฐํฌ
  • ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ ๊ธฐ๋Šฅ ํ…Œ์ŠคํŠธ
import { expect } from 'chai';
import { Contract } from 'ethers';
import { ethers, upgrades } from 'hardhat';
  // contracts
  let boxV2: Contract;

// Start test block
describe('BoxV2', function () {
  beforeEach(async function () {
    const BoxV2 = await ethers.getContractFactory("BoxV2");
    boxV2 = await BoxV2.deploy();
    await boxV2.deployed();
    console.log("boxV2 deployed",boxV2.address);
  });

  // Test case
  it('retrieve returns a value previously stored', async function () {
    // Store a value
    await boxV2.store(42);

    // Test if the returned value is the same one
    // Note that we need to use strings to compare the 256 bit integers
    expect((await boxV2.retrieve()).toString()).to.equal('42');
  });

  // Test case
  it('retrieve returns a value previously incremented', async function () {
    // Increment
    await boxV2.increment();

    // Test if the returned value is the same one
    // Note that we need to use strings to compare the 256 bit integers
    expect((await boxV2.retrieve()).toString()).to.equal('1');
  });
});

TEST4 - /tutorial_hardhat/test/BoxV2Proxy.ts

  • ๋กœ์งV1 ์ปจํŠธ๋ž™ํŠธ ๋ฐฐํฌ & ๋กœ์งV1 ํ”„๋ก์‹œ ๋ฐฐํฌ
  • ๋กœ์งV2 ์ปจํŠธ๋ž™ํŠธ ๋ฐฐํฌ & ๋กœ์งV2๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ
  • ํ”„๋ก์‹œ๋ฅผ ํ†ตํ•ด ์—…๊ทธ๋ ˆ์ด๋“œ ์ •์ƒ ๋™์ž‘ ํ™•์ธ
import { expect } from "chai";
import { Contract } from "ethers";
import { ethers, upgrades } from "hardhat";
// contracts
let boxProxy: Contract;
let boxV2Proxy: Contract;
// Start test block
describe("BoxV2 (proxy)", function () {
  beforeEach(async function () {
    const Box = await ethers.getContractFactory("Box");
    const BoxV2 = await ethers.getContractFactory("BoxV2");

    boxProxy = await upgrades.deployProxy(Box, [42], { initializer: "store" });
    console.log("deployProxy", boxProxy.address);
    let boxV2Implentaion = await upgrades.prepareUpgrade(
      boxProxy.address,
      BoxV2
    );
    console.log("implementation V2 address : ", boxV2Implentaion);
    boxV2Proxy = await upgrades.upgradeProxy(boxProxy.address, BoxV2);
    console.log("upgradeProxy", boxV2Proxy.address);
  });

  // Test case
  it("retrieve returns a value previously incremented", async function () {
    // Increment
    await boxV2Proxy.increment();

    // Test if the returned value is the same one
    // Note that we need to use strings to compare the 256 bit integers
    expect((await boxV2Proxy.retrieve()).toString()).to.equal("43");
  });
});

์ฃผ์˜ ์‚ฌํ•ญ - Storage Collision

์ƒˆ๋กœ์šด ๊ธฐ๋Šฅ์ด๋‚˜ ๋ฒ„๊ทธ ์ˆ˜์ •์œผ๋กœ ์ธํ•ด ์ปจํŠธ๋ž™ํŠธ์˜ ์ƒˆ ๋ฒ„์ „์„ ์ž‘์„ฑํ•  ๋•Œ, ์ค€์ˆ˜ํ•ด์•ผํ•  ์Šคํ† ๋ฆฌ์ง€ ๋ ˆ์ด์•„์›ƒ ์ œํ•œ ์‚ฌํ•ญ์ด ์žˆ๋‹ค. ์ „๋ฐ˜์ ์œผ๋กœ ์ปจํŠธ๋ž™ํŠธ ์ƒํƒœ ๋ณ€์ˆ˜์˜ ์„ ์–ธ ์ˆœ์„œ์™€ ํƒ€์ž…์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์—†๋Š” ๋‚ด์šฉ๋“ค์ด๋‹ค.

์ด๋Ÿฐ ์Šคํ† ๋ฆฌ์ง€ ๋ ˆ์ด์•„์›ƒ ์ œํ•œ์„ ์œ„๋ฐ˜ํ•˜๋ฉด ์—…๊ทธ๋ ˆ์ด๋“œ๋œ ์ปจํŠธ๋ž™ํŠธ์™€ ๊ธฐ์กด ์Šคํ† ๋ฆฌ์ง€์™€์˜ ์Šคํ† ๋ฆฌ์ง€ ์ถฉ๋Œ(Storage Collision)์ด ๋ฐœ์ƒํ•˜์—ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์— ์‹ฌ๊ฐํ•œ ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒ๋  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ ๊ฐ๋ณ„ํ•œ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•˜๋‹ค.

์‹ค์ œ๋กœ ์—…๊ทธ๋ ˆ์ด๋”๋ธ” ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์ž‘์„ฑํ•  ๋•Œ๋Š” ๊ธฐ์กด์˜ ๋กœ์ง ์ปจํŠธ๋ž™ํŠธ๋ฅผ ์ƒ์†ํ•ด์„œ ์ž‘์„ฑํ•˜์—ฌ ๊ธฐ์กด์˜ ๋ณ€์ˆ˜ ์„ ์–ธ์— ๋ณ€ํ™”๊ฐ€ ์—†๋„๋ก ํ•˜๋Š”๊ฒƒ์ด ์ผ๋ฐ˜์ ์ด๋‹ค.

https://forum.openzeppelin.com/t/korean-writing-upgradeable-contracts/2007

์—…๊ทธ๋ ˆ์ด๋”๋ธ” ์ปจํŠธ๋ž™ํŠธ ๊ด€๋ จ ์ฐธ๊ณ 

About

UpgradableLearningKit

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors