Ethereum-Solidity 퀴즈 Q14: 왜 constructors를 upgradeable contracts에서 사용할 수 없나요?
Source: Dev.to
업그레이드 가능한 계약에서의 생성자 동작
Solidity에서는 생성자 내부에 있거나 전역 변수 선언에 포함된 코드는 배포된 계약의 런타임 바이트코드에 포함되지 않습니다. 이 코드는 계약 인스턴스가 배포되는 순간 한 번만 실행됩니다.
프록시 뒤에서 로직 계약을 사용할 경우, 로직 계약의 생성자는 프록시의 스토리지 컨텍스트에서 절대 실행되지 않습니다. 프록시는 생성자에 의해 수행된 스토리지 변경 사항을 전혀 인식하지 못합니다.
해결책: 초기화 함수 사용
계약을 업그레이드 호환으로 만들기 위해, 생성자 로직을 일반 initializer 함수로 옮기고 프록시가 로직 계약에 연결된 뒤 이 함수를 호출합니다. 초기화 함수는 한 번만 호출될 수 있도록 보호되어야 하며, 이는 생성자가 제공하는 일회성 실행 보장을 그대로 반영합니다.
OpenZeppelin Upgrades를 사용하면 프록시를 배포할 때 초기화 함수의 이름(및 매개변수)을 지정할 수 있습니다.
OpenZeppelin Initializable 사용 예시
// contracts/MyContract.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract MyContract is Initializable {
function initialize(
address arg1,
uint256 arg2,
bytes memory arg3
) public payable initializer {
// "constructor" code...
}
}
이 계약은 Initializable을 상속받으며, 이는 initialize 함수를 한 번만 실행할 수 있도록 보장하는 initializer 수정자를 제공합니다.
OpenZeppelin Docs 발췌