OpenZeppelin Initializable中验证address(this)是否为合约的原因探究
问题1:address(this)何时会不是合约?它不应始终指向智能合约实例吗?
在EVM的执行逻辑里,一个地址被判定为合约的核心依据是该地址存储的代码长度是否大于0,AddressUpgradeable.isContract方法正是通过检查目标地址的代码长度来实现判断的。
当合约处于**部署阶段(构造函数执行过程中)**时,虽然合约的地址已经确定(由创建者地址+nonce或CREATE2参数计算得出),但合约的字节码还没有被写入到该地址的存储中,此时address(this)指向的地址代码长度为0,isContract会返回false。这种场景下,address(this)确实是即将成为合约的地址,但在构造函数执行完成前,它还不被EVM认定为合约。
问题2:何时条件(!AddressUpgradeable.isContract(address(this)) && _initialized == 1)会成立?既然_initialized==1表示已初始化,为何要允许该条件通过?
这个条件是为了适配两种常见的开发场景:
构造函数内多次调用初始化函数
如果你的合约构造函数中需要调用多个带有initializer修饰符的函数,第一次调用后_initialized会被设为1,但此时构造函数还在执行,address(this)仍然处于非合约状态(代码未完成部署)。此时后续调用初始化函数时,这个条件会触发,允许函数正常执行,避免因为_initialized已经为1而被阻止。克隆合约(如EIP-1167)的初始化场景
克隆合约通过复制已有合约的字节码快速创建新合约,克隆过程中构造函数不会执行。当你调用克隆合约的初始化函数时,克隆合约正处于部署后的初始化阶段,此时address(this)的代码可能还未完全生效(或在某些实现中仍会被判定为非合约),而如果克隆合约的_initialized已经被设置为1(比如继承自已初始化的实现合约),这个条件可以确保初始化操作能够正常完成。
本质上,这个条件是为了兼容合约部署过程中的特殊状态,避免合法的初始化操作被误拦截。
内容的提问来源于stack exchange,提问作者bzpassersby

