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();
}