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);
}
}
- In what condition,
address(this)would ever not be a contract? Wouldn'taddress(this)always refer to the instance of the smart contract? - 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!
The OpenZeppelin
isContract()function checks ifcodeproperty of the address is non-empty (GitHub).The
codeis still empty while executing a constructor. For example the below code emitsNotContracteven though it's effectively executed from a contract address.Constructor calling 2 functions that both have the
initializermodifier. Or the same function two times (from within the constructor).During the second function call,
_initializedis already1.