SafeProxyFactory
创建 SafeProxy
并且 SafeProxy
继承 Safe
所有方法的关键在于 代理合约(Proxy Pattern),特别是 SafeProxy
充当代理,并将调用委托给 Safe
。让我们解析一下核心机制:
1. SafeProxy
是如何继承 Safe
方法的?
SafeProxy
其实并没有直接继承 Safe
,而是通过 代理(Proxy)模式,利用 delegatecall
让 SafeProxy
具备 Safe
的所有方法。
SafeProxy
的核心逻辑通常在其 fallback
函数内,通过 delegatecall
把调用转发给 _singleton
(也就是 Safe
),让 SafeProxy
看起来像是 Safe
,但实际上只是一个空壳:
2. deployProxy
是如何让 SafeProxy
具备 Safe
方法的?
在 deployProxy
方法中,我们关注以下关键点:
(1) 通过 CREATE2
创建 SafeProxy
bytes memory deploymentData = abi.encodePacked(type(SafeProxy).creationCode, uint256(uint160(_singleton)));
assembly {proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
}
type(SafeProxy).creationCode
:获取SafeProxy
的合约代码。abi.encodePacked(..., uint256(uint160(_singleton)))
:将SafeProxy
代码和_singleton
地址编码到一起。create2(...)
:使用CREATE2
部署SafeProxy
,确保地址可预测。
关键点:这里
_singleton
传入的是Safe
的地址,意味着SafeProxy
内部的singleton
变量会被初始化为Safe
的地址。
(2) SafeProxy
代理 Safe
当 SafeProxy
被创建后,它的 fallback
会将所有调用 delegatecall
到 _singleton
,即 Safe
。因此:
- 从外部调用
SafeProxy
时,会通过fallback
进入delegatecall
,相当于直接调用Safe
本体。 delegatecall
让SafeProxy
继承Safe
的所有逻辑,但存储变量仍然在SafeProxy
内部。
3. initializer
作用
initializer
允许在部署后立即执行初始化,通常用于Safe
的setup
方法,以设置 owners、threshold、fallback handler 等信息。- 这避免了代理合约刚部署时处于未初始化状态,防止攻击者利用
delegatecall
进行恶意初始化(类似EIP-1967
的安全风险)。- 使用
call
执行initializer
:initializer
是一个bytes
类型的 calldata,它通常是Safe
的setup
方法的 ABI 编码参数。call
发送交易到proxy
(SafeProxy),执行initializer
指定的初始化逻辑。
- 如果
call
失败,则回滚整个交易。
各参数解析
这是一个
call
低级操作,Soliditycall
语法如下:参数解析:
参数 作用 gas()
传递当前剩余 Gas 让 proxy
运行initializer
proxy
目标合约地址,即 SafeProxy
实例0
发送的 ETH
数量,这里是 0add(initializer, 0x20)
initializer
的数据指针,跳过前 32 字节长度信息mload(initializer)
initializer
的数据长度0
返回数据存储的内存位置(这里忽略) 0
返回数据的最大长度(这里忽略)
总结
SafeProxy
只是一个壳,所有方法都通过delegatecall
代理到Safe
。deployProxy
通过CREATE2
创建SafeProxy
,并设置Safe
作为singleton
。SafeProxy
的fallback
函数拦截所有调用,并delegatecall
给Safe
,让它表现得像Safe
。initializer
允许部署时执行Safe
的setup
,确保合约立即可用。
最终结果: 通过 SafeProxy
交互,相当于直接调用 Safe
,但存储数据仍然保留在 SafeProxy
自己的存储空间内,这就是 代理模式(Proxy Pattern) 的强大之处。 🚀