抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

前言

最近在做 damn defi 靶场的时候,遇到了一些题涉及到 GnosisSafeProxy 相关知识的题。查缺补漏,先来学习学习 GnosisSafeProxy 合约。

解读GnosisSafeProxy.sol

这是一个代理合约,一个构造器一个回调函数。

1. 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;

/// @title IProxy - Helper interface to access masterCopy of the Proxy on-chain
/// @author Richard Meissner - <richard@gnosis.io>
interface IProxy {
function masterCopy() external view returns (address);
}

/// @title GnosisSafeProxy - Generic proxy contract allows to execute all transactions applying the code of a master contract.
/// @author Stefan George - <stefan@gnosis.io>
/// @author Richard Meissner - <richard@gnosis.io>
contract GnosisSafeProxy {
// singleton always needs to be first declared variable, to ensure that it is at the same location in the contracts to which calls are delegated.
// To reduce deployment costs this variable is internal and needs to be retrieved via `getStorageAt`
address internal singleton; // 为了防止插槽冲突

/// @dev Constructor function sets address of singleton contract.
/// @param _singleton Singleton address.
constructor(address _singleton) {
require(_singleton != address(0), "Invalid singleton address provided");
singleton = _singleton; // 初始化singleton
}

/// @dev Fallback function forwards all transactions and returns all received return data.
fallback() external payable {
// solhint-disable-next-line no-inline-assembly
assembly {
let _singleton := and(sload(0), 0xffffffffffffffffffffffffffffffffffffffff)
// 0xa619486e == keccak("masterCopy()"). The value is right padded to 32-bytes with 0s
if eq(calldataload(0), 0xa619486e00000000000000000000000000000000000000000000000000000000) {
mstore(0, _singleton)
return(0, 0x20)
}
calldatacopy(0, 0, calldatasize())
let success := delegatecall(gas(), _singleton, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
if eq(success, 0) {
revert(0, returndatasize())
}
return(0, returndatasize())
}
}
}

2. 解读

回调函数,本质上是其他的代理合约差不多,采用了内联汇编的 delegatecall 操作码,调用的是 singleton合约的函数,效果作用在代理合约上。之前已经对代理合约有过一定的 学习

解读IProxyCreationCallback.sol

1. 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import "./GnosisSafeProxy.sol";

interface IProxyCreationCallback {
function proxyCreated(
GnosisSafeProxy proxy,
address _singleton,
bytes calldata initializer,
uint256 saltNonce
) external;
}

2. 解读

提供了一个用于创建代理合约的函数。

解读GnosisSafeProxyFactory.sol

该函数继承了

1
2
import "./GnosisSafeProxy.sol";
import "./IProxyCreationCallback.sol";

1. 源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;

import "./GnosisSafeProxy.sol";
import "./IProxyCreationCallback.sol";

/// @title Proxy Factory - Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
/// @author Stefan George - <stefan@gnosis.pm>
contract GnosisSafeProxyFactory {
event ProxyCreation(GnosisSafeProxy proxy, address singleton);

/// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
/// @param singleton Address of singleton contract.
/// @param data Payload for message call sent to new proxy contract.
function createProxy(address singleton, bytes memory data) public returns (GnosisSafeProxy proxy) {
proxy = new GnosisSafeProxy(singleton); // 创建代理合约
if (data.length > 0)
// solhint-disable-next-line no-inline-assembly
assembly {
if eq(call(gas(), proxy, 0, add(data, 0x20), mload(data), 0, 0), 0) {
revert(0, 0)
}
}
emit ProxyCreation(proxy, singleton);
}

/// @dev Allows to retrieve the runtime code of a deployed Proxy. This can be used to check that the expected Proxy was deployed.
function proxyRuntimeCode() public pure returns (bytes memory) {
return type(GnosisSafeProxy).runtimeCode;
}

/// @dev Allows to retrieve the creation code used for the Proxy deployment. With this it is easily possible to calculate predicted address.
function proxyCreationCode() public pure returns (bytes memory) {
return type(GnosisSafeProxy).creationCode;
}

/// @dev Allows to create new proxy contact using CREATE2 but it doesn't run the initializer.
/// This method is only meant as an utility to be called from other methods
/// @param _singleton Address of singleton contract.
/// @param initializer Payload for message call sent to new proxy contract.
/// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
function deployProxyWithNonce(
address _singleton,
bytes memory initializer,
uint256 saltNonce
) internal returns (GnosisSafeProxy proxy) {
// If the initializer changes the proxy address should change too. Hashing the initializer data is cheaper than just concatinating it
bytes32 salt = keccak256(abi.encodePacked(keccak256(initializer), saltNonce));
bytes memory deploymentData = abi.encodePacked(type(GnosisSafeProxy).creationCode, uint256(uint160(_singleton)));
// solhint-disable-next-line no-inline-assembly
assembly {
proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
}
require(address(proxy) != address(0), "Create2 call failed");
}

/// @dev Allows to create new proxy contact and execute a message call to the new proxy within one transaction.
/// @param _singleton Address of singleton contract.
/// @param initializer Payload for message call sent to new proxy contract.
/// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
function createProxyWithNonce(
address _singleton,
bytes memory initializer,
uint256 saltNonce
) public returns (GnosisSafeProxy proxy) {
proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
if (initializer.length > 0)
// solhint-disable-next-line no-inline-assembly
assembly {
if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) {
revert(0, 0)
}
}
emit ProxyCreation(proxy, _singleton);
}

/// @dev Allows to create new proxy contact, execute a message call to the new proxy and call a specified callback within one transaction
/// @param _singleton Address of singleton contract.
/// @param initializer Payload for message call sent to new proxy contract.
/// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
/// @param callback Callback that will be invoced after the new proxy contract has been successfully deployed and initialized.
function createProxyWithCallback(
address _singleton,
bytes memory initializer,
uint256 saltNonce,
IProxyCreationCallback callback
) public returns (GnosisSafeProxy proxy) {
uint256 saltNonceWithCallback = uint256(keccak256(abi.encodePacked(saltNonce, callback)));
proxy = createProxyWithNonce(_singleton, initializer, saltNonceWithCallback);
if (address(callback) != address(0)) callback.proxyCreated(proxy, _singleton, initializer, saltNonce);
}

/// @dev Allows to get the address for a new proxy contact created via `createProxyWithNonce`
/// This method is only meant for address calculation purpose when you use an initializer that would revert,
/// therefore the response is returned with a revert. When calling this method set `from` to the address of the proxy factory.
/// @param _singleton Address of singleton contract.
/// @param initializer Payload for message call sent to new proxy contract.
/// @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
function calculateCreateProxyWithNonceAddress(
address _singleton,
bytes calldata initializer,
uint256 saltNonce
) external returns (GnosisSafeProxy proxy) {
proxy = deployProxyWithNonce(_singleton, initializer, saltNonce);
revert(string(abi.encodePacked(proxy)));
}
}

2. 解读

createProxy函数

其作用是创建一个代理合约对象,可以执行一次委托调用。

proxyRuntimeCode函数

AI解读:

type(GnosisSafeProxy) 返回的是一个包含合约类型信息的对象,它可以用于访问合约的各种属性和方法。在这个代码片段中,使用了 type(GnosisSafeProxy).runtimeCode 来获取 GnosisSafeProxy 合约的运行时字节码。

运行时字节码是 Solidity 合约编译后的字节码,它包含了合约的代码和数据,并且已经经过了编译器和优化器的处理,可以直接在以太坊虚拟机(EVM)上执行。与源代码不同,运行时字节码是无法进行修改的,因此可以用于进行合约验证和安全审计。

在这个代码片段中,type(GnosisSafeProxy).runtimeCode 返回的是 GnosisSafeProxy 合约的运行时字节码。这个运行时字节码可以用于生成代理合约的 bytecode,从而创建新的 GnosisSafe 多签钱包合约实例。具体来说,这个运行时字节码包含了合约的代码和数据,以及合约的地址等元信息,可以用于部署合约和执行合约操作。

proxyCreationCode函数

返回 GnosisSafeProxy 的 Bytecode。

deployProxyWithNonce函数

使用create2的方式生成一个GnosisSafeProxy合约地址。

createProxyWithNonce函数

调用 deployProxyWithNonce 函数,创建一个新的代理合约地址,同时也可以执行一次委托调用。

createProxyWithCallback函数

根据 callback采用create2的方式计算出代理合约地址

calculateCreateProxyWithNonceAddress函数

根据 saltNonce采用create2的方式计算出代理合约地址。

最后,作者水平有限,有错请大佬们及时指出😁

评论



政策 · 统计 | 本站使用 Volantis 主题设计