Upgradable Contract
개념
contract를 배포하고나서 contract의 로직을 업데이트할 수 있도록 설계된 계약을 말한다.
사용자와 상호작용하는 proxy contract와 실제 구현을 담고 있는 implementation contract로 구성된다.
proxy contract는 데이터를 보관하고 있고 사용자의 요청이 오면
delegate call을 통해 implementation contract에게 위임한다.
implementation contract는 함수를 보관하고 있고 새로운 로직이 필요하면 새로운 주소로 배포된다.

OpenZeppelin Contract
Proxy
사용자가 proxy contract에서 함수를 호출하면
실제 함수는 implementation contract에 있기 때문에 fallback 함수가 호출된다.
fallback 함수에서는 delegate call을 통해 위임해주는 것을 볼 수 있다.
function _fallback() internal virtual {
_delegate(_implementation());
}
function _delegate(address implementation) internal virtual {
assembly {
calldatacopy(0x00, 0x00, calldatasize())
let result := delegatecall(gas(), implementation, 0x00, calldatasize(), 0x00, 0x00)
returndatacopy(0x00, 0x00, returndatasize())
switch result
case 0 {
revert(0x00, returndatasize())
}
default {
return(0x00, returndatasize())
}
}
}
테스트
아래와 같은 implementation contract 2개를 만들었다.
BoxV2에서는 increment 함수를 추가했다.
contract Box {
uint256 private value;
event ValueChanged(uint256 newValue);
function store(uint256 newValue) public {
value = newValue;
emit ValueChanged(newValue);
}
function retrieve() public view returns (uint256) {
return value;
}
}
contract BoxV2 {
uint256 private value;
event ValueChanged(uint256 newValue);
function store(uint256 newValue) public {
value = newValue;
emit ValueChanged(newValue);
}
function retrieve() public view returns (uint256) {
return value;
}
function increment() public {
value = value + 1;
emit ValueChanged(value);
}
}
proxy contract를 배포하고 store 함수를 호출해서 42를 value로 가지게 한다.
이후 implementation contract를 box v2 contract로 upgrade하고
increment 함수를 호출해서 43으로 변경이 되었는지 검증한다.
let boxContract: Contract;
let boxV2Contract: Contract;
describe('Test Proxy Contract', function () {
beforeEach(async function () {
const Box = await ethers.getContractFactory("Box");
const BoxV2 = await ethers.getContractFactory("BoxV2");
boxContract = await upgrades.deployProxy(Box, [42], {initializer: 'store'});
await upgrades.prepareUpgrade(boxContract.address, BoxV2);
boxV2Contract = await upgrades.upgradeProxy(boxContract.address, BoxV2);
});
it('retrieve returns a value previously incremented', async function () {
await boxV2Contract.increment();
expect((await boxV2Contract.retrieve()).toString()).to.equal('43');
});
});'분석 > 블록체인' 카테고리의 다른 글
| [Ethereum] Uniswap V2에 대해 알아보자 (0) | 2025.10.12 |
|---|---|
| [Ethereum] Multisig에 대해 알아보자 (0) | 2025.10.08 |
| [Ethereum] ERC-721A에 대해 알아보자 (0) | 2025.10.06 |
| [Ethereum] EIP-712에 대해 알아보자 (0) | 2025.10.02 |
| [Ethereum] ERC-1155에 대해 알아보자 (0) | 2025.10.01 |