BlockChain/solidity
[solidity/openzepplin] initializable.sol 분석
Yuyudev
2024. 9. 25. 21:44
몇 줄이 안되지만 필요하다.
변수명 설명
uint8 private | _initialized | 초기화가 되었는지 기본 0 |
bool private | _initializing | 초기화하는 중인지 기본 false |
pragma solidity ^0.8.2;
import "../../utils/AddressUpgradeable.sol";
abstract contract Initializable {
uint8 private _initialized;
bool private _initializing;
event Initialized(uint8 version);
modifier initializer() {
bool isTopLevelCall = !_initializing;
require(
(isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
"Initializable: contract is already initialized"
);
_initialized = 1;
if (isTopLevelCall) {
_initializing = true;
}
_;
if (isTopLevelCall) {
_initializing = false;
emit Initialized(1);
}
}
modifier reinitializer(uint8 version) {
require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
_initialized = version;
_initializing = true;
_;
_initializing = false;
emit Initialized(version);
}
modifier onlyInitializing() {
require(_initializing, "Initializable: contract is not initializing");
_;
}
function _disableInitializers() internal virtual {
require(!_initializing, "Initializable: contract is initializing");
if (_initialized < type(uint8).max) {
_initialized = type(uint8).max;
emit Initialized(type(uint8).max);
}
}
function _getInitializedVersion() internal view returns (uint8) {
return _initialized;
}
function _isInitializing() internal view returns (bool) {
return _initializing;
}
}
openzepplin에 쓰여있는 거 해석
이 컨트랙트는 upgradeable contract를 작성하는데 도움을 주는 기본적인 컨트랙트이다. 또는 프록시를 이용한 배포되는 컨트랙트의 어떠한 종류에도 사용할 수 있다. 포록시 컨트랙트들은 constructor을 만들지 않기 때문에 constructor 로직을 흔히 'initialize'라고 불리는 바깥의 초기화 함수로 옮길 수 있다. 한 번만 불리게 함으로써 이 initializer 함수를 보호하는 것이 필요하다. 이 컨트랙트의 modifier의 'initializer' 이러한 작용을 한다.
initialization 함수는 버전 넘버를 제공한다. 한 번 버전이 쓰이면 다시 쓰일 수 없다. 이러한 메커니즘은 각 단계의 재 실행을 막는다. 그러나 초기화하기 위해 필요한 모듈을 추가하는 업그레이드의 경우 새로운 초기화 단계의 생성을 허용한다.(?)<-의역;;
contract MyToken is ERC20Upgradeable {
function initialize() initializer public {
__ERC20_init("MyToken", "MTK");
}
}
contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
function initializeV2() reinitializer(2) public {
__ERC20Permit_init("MyToken");
}
}
초기화되지 않은 프록시를 남기지 않기 위해서는 initializer 함수가 가능한 빨리 실행되어야 한다.
{ERC1967Proxy-constructor}에서 논의가 되었던 '_data'로 불리는 encode 함수를 제공함으로써
상속에서 사용할 때,
이것은 ㅈ2ㅏ동적으로 constructor로
[주의]
초기화되지 않은 contract를 두지 말아라.
초기화되지 않은 contract는 해커에 의해 공격받는다. 이것은 프록시 임팩트를 미치는 프록시 컨트랙트와 실행 컨트랙트 둘 다에게 해당이 된다. implement contract가 사용되는 걸 막기 위해서, 배포할 때 반드시 _disableInitializers 함수(자동적으로 constructor가 잠기는) 를 호출해야만 한다.
constructor() {
_disableInitializers();
}