-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathACOFactory.sol
269 lines (242 loc) · 10.9 KB
/
ACOFactory.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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
pragma solidity ^0.6.6;
import "../libs/Address.sol";
import "../libs/BokkyPooBahsDateTimeLibrary.sol";
import "../libs/Strings.sol";
/**
* @title ACOFactory
* @dev The contract is the implementation for the ACOProxy.
*/
contract ACOFactory {
/**
* @dev Emitted when the factory admin address has been changed.
* @param previousFactoryAdmin Address of the previous factory admin.
* @param newFactoryAdmin Address of the new factory admin.
*/
event SetFactoryAdmin(address previousFactoryAdmin, address newFactoryAdmin);
/**
* @dev Emitted when the ACO token implementation has been changed.
* @param previousAcoTokenImplementation Address of the previous ACO token implementation.
* @param newAcoTokenImplementation Address of the new ACO token implementation.
*/
event SetAcoTokenImplementation(address previousAcoTokenImplementation, address newAcoTokenImplementation);
/**
* @dev Emitted when the ACO fee has been changed.
* @param previousAcoFee Value of the previous ACO fee.
* @param newAcoFee Value of the new ACO fee.
*/
event SetAcoFee(uint256 previousAcoFee, uint256 newAcoFee);
/**
* @dev Emitted when the ACO fee destination address has been changed.
* @param previousAcoFeeDestination Address of the previous ACO fee destination.
* @param newAcoFeeDestination Address of the new ACO fee destination.
*/
event SetAcoFeeDestination(address previousAcoFeeDestination, address newAcoFeeDestination);
/**
* @dev Emitted when a new ACO token has been created.
* @param underlying Address of the underlying asset (0x0 for Ethereum).
* @param strikeAsset Address of the strike asset (0x0 for Ethereum).
* @param isCall True if the type is CALL, false for PUT.
* @param strikePrice The strike price with the strike asset precision.
* @param expiryTime The UNIX time for the ACO token expiration.
* @param acoToken Address of the new ACO token created.
* @param acoTokenImplementation Address of the ACO token implementation used on creation.
*/
event NewAcoToken(address indexed underlying, address indexed strikeAsset, bool indexed isCall, uint256 strikePrice, uint256 expiryTime, address acoToken, address acoTokenImplementation);
/**
* @dev The ACO fee value.
* It is a percentage value (100000 is 100%).
*/
uint256 public acoFee;
/**
* @dev The factory admin address.
*/
address public factoryAdmin;
/**
* @dev The ACO token implementation address.
*/
address public acoTokenImplementation;
/**
* @dev The ACO fee destination address.
*/
address public acoFeeDestination;
/**
* @dev Modifier to check if the `msg.sender` is the factory admin.
* Only factory admin address can execute.
*/
modifier onlyFactoryAdmin() {
require(msg.sender == factoryAdmin, "ACOFactory::onlyFactoryAdmin");
_;
}
/**
* @dev Function to initialize the contract.
* It should be called through the `data` argument when creating the proxy.
* It must be called only once. The `assert` is to guarantee that behavior.
* @param _factoryAdmin Address of the factory admin.
* @param _acoTokenImplementation Address of the ACO token implementation.
* @param _acoFee Value of the ACO fee.
* @param _acoFeeDestination Address of the ACO fee destination.
*/
function init(address _factoryAdmin, address _acoTokenImplementation, uint256 _acoFee, address _acoFeeDestination) public {
require(factoryAdmin == address(0) && acoTokenImplementation == address(0), "ACOFactory::init: Contract already initialized.");
_setFactoryAdmin(_factoryAdmin);
_setAcoTokenImplementation(_acoTokenImplementation);
_setAcoFee(_acoFee);
_setAcoFeeDestination(_acoFeeDestination);
}
/**
* @dev Function to guarantee that the contract will not receive ether.
*/
receive() external payable virtual {
revert();
}
/**
* @dev Function to create a new ACO token.
* It deploys a minimal proxy for the ACO token implementation address.
* @param underlying Address of the underlying asset (0x0 for Ethereum).
* @param strikeAsset Address of the strike asset (0x0 for Ethereum).
* @param isCall Whether the ACO token is the Call type.
* @param strikePrice The strike price with the strike asset precision.
* @param expiryTime The UNIX time for the ACO token expiration.
*/
function createAcoToken(
address underlying,
address strikeAsset,
bool isCall,
uint256 strikePrice,
uint256 expiryTime
) onlyFactoryAdmin external virtual {
address acoToken = _deployAcoToken(_getAcoTokenInitData(underlying, strikeAsset, isCall, strikePrice, expiryTime));
emit NewAcoToken(underlying, strikeAsset, isCall, strikePrice, expiryTime, acoToken, acoTokenImplementation);
}
/**
* @dev Function to set the factory admin address.
* Only can be called by the factory admin.
* @param newFactoryAdmin Address of the new factory admin.
*/
function setFactoryAdmin(address newFactoryAdmin) onlyFactoryAdmin external virtual {
_setFactoryAdmin(newFactoryAdmin);
}
/**
* @dev Function to set the ACO token implementation address.
* Only can be called by the factory admin.
* @param newAcoTokenImplementation Address of the new ACO token implementation.
*/
function setAcoTokenImplementation(address newAcoTokenImplementation) onlyFactoryAdmin external virtual {
_setAcoTokenImplementation(newAcoTokenImplementation);
}
/**
* @dev Function to set the ACO fee.
* Only can be called by the factory admin.
* @param newAcoFee Value of the new ACO fee. It is a percentage value (100000 is 100%).
*/
function setAcoFee(uint256 newAcoFee) onlyFactoryAdmin external virtual {
_setAcoFee(newAcoFee);
}
/**
* @dev Function to set the ACO destination address.
* Only can be called by the factory admin.
* @param newAcoFeeDestination Address of the new ACO destination.
*/
function setAcoFeeDestination(address newAcoFeeDestination) onlyFactoryAdmin external virtual {
_setAcoFeeDestination(newAcoFeeDestination);
}
/**
* @dev Internal function to set the factory admin address.
* @param newFactoryAdmin Address of the new factory admin.
*/
function _setFactoryAdmin(address newFactoryAdmin) internal virtual {
require(newFactoryAdmin != address(0), "ACOFactory::_setFactoryAdmin: Invalid factory admin");
emit SetFactoryAdmin(factoryAdmin, newFactoryAdmin);
factoryAdmin = newFactoryAdmin;
}
/**
* @dev Internal function to set the ACO token implementation address.
* @param newAcoTokenImplementation Address of the new ACO token implementation.
*/
function _setAcoTokenImplementation(address newAcoTokenImplementation) internal virtual {
require(Address.isContract(newAcoTokenImplementation), "ACOFactory::_setAcoTokenImplementation: Invalid ACO token implementation");
emit SetAcoTokenImplementation(acoTokenImplementation, newAcoTokenImplementation);
acoTokenImplementation = newAcoTokenImplementation;
}
/**
* @dev Internal function to set the ACO fee.
* @param newAcoFee Value of the new ACO fee. It is a percentage value (100000 is 100%).
*/
function _setAcoFee(uint256 newAcoFee) internal virtual {
emit SetAcoFee(acoFee, newAcoFee);
acoFee = newAcoFee;
}
/**
* @dev Internal function to set the ACO destination address.
* @param newAcoFeeDestination Address of the new ACO destination.
*/
function _setAcoFeeDestination(address newAcoFeeDestination) internal virtual {
require(newAcoFeeDestination != address(0), "ACOFactory::_setAcoFeeDestination: Invalid ACO fee destination");
emit SetAcoFeeDestination(acoFeeDestination, newAcoFeeDestination);
acoFeeDestination = newAcoFeeDestination;
}
/**
* @dev Internal function to get the ACO token initialize data.
* @param underlying Address of the underlying asset (0x0 for Ethereum).
* @param strikeAsset Address of the strike asset (0x0 for Ethereum).
* @param isCall True if the type is CALL, false for PUT.
* @param strikePrice The strike price with the strike asset precision.
* @param expiryTime The UNIX time for the ACO token expiration.
* @return ABI encoded with signature for initializing ACO token.
*/
function _getAcoTokenInitData(
address underlying,
address strikeAsset,
bool isCall,
uint256 strikePrice,
uint256 expiryTime
) internal view virtual returns(bytes memory) {
return abi.encodeWithSignature("init(address,address,bool,uint256,uint256,uint256,address)",
underlying,
strikeAsset,
isCall,
strikePrice,
expiryTime,
acoFee,
acoFeeDestination
);
}
/**
* @dev Internal function to deploy a minimal proxy using ACO token implementation.
* @param initData ABI encoded with signature for initializing the new ACO token.
* @return Address of the new minimal proxy deployed for the ACO token.
*/
function _deployAcoToken(bytes memory initData) internal virtual returns(address) {
require(initData.length > 0, "ACOFactory::_deployToken: Invalid init data");
bytes20 implentationBytes = bytes20(acoTokenImplementation);
address proxy;
assembly {
let clone := mload(0x40)
mstore(clone, 0x3d602d80600a3d3981f3363d3d373d3d3d363d73000000000000000000000000)
mstore(add(clone, 0x14), implentationBytes)
mstore(add(clone, 0x28), 0x5af43d82803e903d91602b57fd5bf30000000000000000000000000000000000)
proxy := create(0, clone, 0x37)
}
(bool success, bytes memory returnData) = proxy.call(initData);
require(success, _acoTokenInititalizeError(returnData));
return proxy;
}
/**
* @dev Internal function to handle the return data on initializing ACO token with an error.
* 4 bytes (function signature) + 32 bytes (offset) + 32 bytes (error string length) + X bytes (error string)
* @param data Returned data with an error.
* @return String with the error.
*/
function _acoTokenInititalizeError(bytes memory data) internal pure virtual returns(string memory) {
if (data.length >= 100) {
bytes memory buffer = new bytes(data.length - 68);
uint256 index = 0;
for (uint256 i = 68; i < data.length; ++i) {
buffer[index++] = data[i];
}
return string(buffer);
} else {
return "ACOFactory::_acoTokenInititalizeError";
}
}
}