-
Notifications
You must be signed in to change notification settings - Fork 1.1k
Expand file tree
/
Copy pathSafeProxyFactory.sol
More file actions
129 lines (118 loc) · 6.44 KB
/
SafeProxyFactory.sol
File metadata and controls
129 lines (118 loc) · 6.44 KB
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
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
// SPDX-License-Identifier: LGPL-3.0-only
pragma solidity >=0.7.0 <0.9.0;
import "./SafeProxy.sol";
import "./IProxyCreationCallback.sol";
/**
* @title Proxy Factory - Allows to create a new proxy contract and execute a message call to the new proxy within one transaction.
* @author Stefan George - @Georgi87
*/
contract SafeProxyFactory {
event ProxyCreation(SafeProxy indexed proxy, address singleton);
/// @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(SafeProxy).creationCode;
}
/**
* @notice Internal method to create a new proxy contract using CREATE2. Optionally executes an initializer call to a new proxy.
* @param _singleton Address of singleton contract. Must be deployed at the time of execution.
* @param initializer (Optional) Payload for a message call to be sent to a new proxy contract.
* @param salt Create2 salt to use for calculating the address of the new proxy contract.
* @return proxy Address of the new proxy contract.
*/
function deployProxy(address _singleton, bytes memory initializer, bytes32 salt) internal returns (SafeProxy proxy) {
require(isContract(_singleton), "Singleton contract not deployed");
bytes memory deploymentData = abi.encodePacked(type(SafeProxy).creationCode, uint256(uint160(_singleton)));
// solhint-disable-next-line no-inline-assembly
/// @solidity memory-safe-assembly
assembly {
proxy := create2(0x0, add(0x20, deploymentData), mload(deploymentData), salt)
}
require(address(proxy) != address(0), "Create2 call failed");
if (initializer.length > 0) {
// solhint-disable-next-line no-inline-assembly
/// @solidity memory-safe-assembly
assembly {
if eq(call(gas(), proxy, 0, add(initializer, 0x20), mload(initializer), 0, 0), 0) {
revert(0, 0)
}
}
}
}
/**
* @notice Deploys a new proxy with `_singleton` singleton and `saltNonce` salt. Optionally executes an initializer call to a new proxy.
* @param _singleton Address of singleton contract. Must be deployed at the time of execution.
* @param initializer Payload for a message call to be sent to a 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 (SafeProxy 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));
proxy = deployProxy(_singleton, initializer, salt);
emit ProxyCreation(proxy, _singleton);
}
/**
* @notice Deploys a new chain-specific proxy with `_singleton` singleton and `saltNonce` salt. Optionally executes an initializer call to a new proxy.
* @dev Allows to create a new proxy contract that should exist only on 1 network (e.g. specific governance or admin accounts)
* by including the chain id in the create2 salt. Such proxies cannot be created on other networks by replaying the transaction.
* @param _singleton Address of singleton contract. Must be deployed at the time of execution.
* @param initializer Payload for a message call to be sent to a new proxy contract.
* @param saltNonce Nonce that will be used to generate the salt to calculate the address of the new proxy contract.
*/
function createChainSpecificProxyWithNonce(
address _singleton,
bytes memory initializer,
uint256 saltNonce
) public returns (SafeProxy 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, getChainId()));
proxy = deployProxy(_singleton, initializer, salt);
emit ProxyCreation(proxy, _singleton);
}
/**
* @notice Deploy a new proxy with `_singleton` singleton and `saltNonce` salt.
* Optionally executes an initializer call to a new proxy and calls a specified callback address `callback`.
* @param _singleton Address of singleton contract. Must be deployed at the time of execution.
* @param initializer Payload for a message call to be sent to a 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 invoked after the new proxy contract has been successfully deployed and initialized.
*/
function createProxyWithCallback(
address _singleton,
bytes memory initializer,
uint256 saltNonce,
IProxyCreationCallback callback
) public returns (SafeProxy 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);
}
/**
* @notice Returns true if `account` is a contract.
* @dev This function will return false if invoked during the constructor of a contract,
* as the code is not actually created until after the constructor finishes.
* @param account The address being queried
* @return True if `account` is a contract
*/
function isContract(address account) internal view returns (bool) {
uint256 size;
// solhint-disable-next-line no-inline-assembly
/// @solidity memory-safe-assembly
assembly {
size := extcodesize(account)
}
return size > 0;
}
/**
* @notice Returns the ID of the chain the contract is currently deployed on.
* @return The ID of the current chain as a uint256.
*/
function getChainId() public view returns (uint256) {
uint256 id;
// solhint-disable-next-line no-inline-assembly
/// @solidity memory-safe-assembly
assembly {
id := chainid()
}
return id;
}
}