-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathACOProxy.sol
149 lines (129 loc) · 4.9 KB
/
ACOProxy.sol
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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
pragma solidity ^0.6.6;
import "../libs/Address.sol";
/**
* @title ACOProxy
* @dev A proxy contract that implements delegation of calls to other contracts.
*/
contract ACOProxy {
/**
* @dev Emitted when the admin address has been changed.
* @param previousAdmin Address of the previous admin.
* @param newAdmin Address of the new admin.
*/
event ProxyAdminUpdated(address previousAdmin, address newAdmin);
/**
* @dev Emitted when the proxy implementation has been changed.
* @param previousImplementation Address of the previous proxy implementation.
* @param newImplementation Address of the new proxy implementation.
*/
event SetImplementation(address previousImplementation, address newImplementation);
/**
* @dev Storage position for the admin address.
*/
bytes32 private constant adminPosition = keccak256("acoproxy.admin");
/**
* @dev Storage position for the proxy implementation address.
*/
bytes32 private constant implementationPosition = keccak256("acoproxy.implementation");
/**
* @dev Modifier to check if the `msg.sender` is the admin.
* Only admin address can execute.
*/
modifier onlyAdmin() {
require(msg.sender == admin(), "ACOProxy::onlyAdmin");
_;
}
constructor(address _admin, address _implementation, bytes memory _initdata) public {
_setAdmin(_admin);
_setImplementation(_implementation, _initdata);
}
/**
* @dev Fallback function that delegates the execution to the proxy implementation contract.
*/
fallback() external payable {
address addr = implementation();
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), addr, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
/**
* @dev Function to be compliance with EIP 897.
* https://github.com/ethereum/EIPs/blob/master/EIPS/eip-897.md
* It is an "upgradable proxy".
*/
function proxyType() public pure returns(uint256) {
return 2;
}
/**
* @dev Function to get the proxy admin address.
* @return adm The proxy admin address.
*/
function admin() public view returns (address adm) {
bytes32 position = adminPosition;
assembly {
adm := sload(position)
}
}
/**
* @dev Function to get the proxy implementation address.
* @return impl The proxy implementation address.
*/
function implementation() public view returns (address impl) {
bytes32 position = implementationPosition;
assembly {
impl := sload(position)
}
}
/**
* @dev Function to set the proxy admin address.
* Only can be called by the proxy admin.
* @param newAdmin Address of the new proxy admin.
*/
function transferProxyAdmin(address newAdmin) external onlyAdmin {
_setAdmin(newAdmin);
}
/**
* @dev Function to set the proxy implementation address.
* Only can be called by the proxy admin.
* @param newImplementation Address of the new proxy implementation.
* @param initData ABI encoded with signature data that will be delegated over the new implementation.
*/
function setImplementation(address newImplementation, bytes calldata initData) external onlyAdmin {
_setImplementation(newImplementation, initData);
}
/**
* @dev Internal function to set the proxy admin address.
* @param newAdmin Address of the new proxy admin.
*/
function _setAdmin(address newAdmin) internal {
require(newAdmin != address(0), "ACOProxy::_setAdmin: Invalid admin");
emit ProxyAdminUpdated(admin(), newAdmin);
bytes32 position = adminPosition;
assembly {
sstore(position, newAdmin)
}
}
/**
* @dev Internal function to set the proxy implementation address.
* The implementation address must be a contract.
* @param newImplementation Address of the new proxy implementation.
* @param initData ABI encoded with signature data that will be delegated over the new implementation.
*/
function _setImplementation(address newImplementation, bytes memory initData) internal {
require(Address.isContract(newImplementation), "ACOProxy::_setImplementation: Invalid implementation");
emit SetImplementation(implementation(), newImplementation);
bytes32 position = implementationPosition;
assembly {
sstore(position, newImplementation)
}
if (initData.length > 0) {
(bool success,) = newImplementation.delegatecall(initData);
assert(success);
}
}
}