Search code examples
solidityinitializeropenzeppelin

In solidity, what's the need of verifying 'address(this)' is a contract?


In openzeppelin's Initializable.sol library(v4.7.0), there is a modifier initializer() that checks if a function has already been initialized. In the require statement, it checks the value of _initialized which I understand. But in addition, it also verifies whether address(this) is a contract or not through AddressUpgradeable.isContract(address(this). Why is this needed? See code and questions below.

    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);
    }
}
  1. In what condition, address(this) would ever not be a contract? Wouldn't address(this) always refer to the instance of the smart contract?
  2. In what condition (!AddressUpgradeable.isContract(address(this)) && _initialized == 1) will return true? Because if _initialized==1, it means the function has been initialized. Then why would we still allow this extra condition to pass? Thank you!

Solution

  • 1. In what condition, address(this) would ever not be a contract?

    The OpenZeppelin isContract() function checks if code property of the address is non-empty (GitHub).

    The code is still empty while executing a constructor. For example the below code emits NotContract even though it's effectively executed from a contract address.

    pragma solidity ^0.8;
    
    contract Caller {
        event IsContract();
        event NotContract();
    
        constructor(Target target) {
            if (target.isCallerContract()) {
                emit IsContract();
            } else {
                emit NotContract();
            }
        }
    }
    
    contract Target {
        function isCallerContract() external view returns (bool) {
            return msg.sender.code.length != 0;
        }
    }
    

    In what condition (!AddressUpgradeable.isContract(address(this)) && _initialized == 1) will return true?

    Constructor calling 2 functions that both have the initializer modifier. Or the same function two times (from within the constructor).

    During the second function call, _initialized is already 1.