This repository has been archived by the owner on May 15, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
ArrakisMetaVault.sol
249 lines (195 loc) · 7.5 KB
/
ArrakisMetaVault.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
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.19;
import {IArrakisMetaVault} from "../interfaces/IArrakisMetaVault.sol";
import {IArrakisLPModule} from "../interfaces/IArrakisLPModule.sol";
import {IModuleRegistry} from "../interfaces/IModuleRegistry.sol";
import {BASE} from "../constants/CArrakis.sol";
import {ReentrancyGuard} from
"@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {EnumerableSet} from
"@openzeppelin/contracts/utils/structs/EnumerableSet.sol";
import {Initializable} from
"@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
abstract contract ArrakisMetaVault is
IArrakisMetaVault,
ReentrancyGuard,
Initializable
{
using EnumerableSet for EnumerableSet.AddressSet;
// #region immutable properties.
address public immutable moduleRegistry;
address public immutable token0;
address public immutable token1;
// #endregion immutable properties.
// #region public properties.
IArrakisLPModule public module;
address public manager;
// #endregion public properties.
EnumerableSet.AddressSet internal _whitelistedModules;
// #region modifier.
modifier onlyOwnerCustom() {
_onlyOwnerCheck();
_;
}
modifier onlyManager() {
if (msg.sender != manager) {
revert OnlyManager(msg.sender, manager);
}
_;
}
// #endregion modifier.
constructor(
address moduleRegistry_,
address manager_,
address token0_,
address token1_
) {
// #region checks.
if (moduleRegistry_ == address(0)) {
revert AddressZero("Module Registry");
}
if (manager_ == address(0)) revert AddressZero("Manager");
if (token0_ == address(0)) revert AddressZero("Token 0");
if (token1_ == address(0)) revert AddressZero("Token 1");
if (token0_ > token1_) revert Token0GtToken1();
if (token0_ == token1_) revert Token0EqToken1();
// #endregion checks.
moduleRegistry = moduleRegistry_;
manager = manager_;
token0 = token0_;
token1 = token1_;
emit LogSetManager(manager_);
}
function initialize(address module_) external initializer {
if (module_ == address(0)) revert AddressZero("Module");
_whitelistedModules.add(module_);
module = IArrakisLPModule(module_);
emit LogSetFirstModule(module_);
emit LogWhitelistedModule(module_);
}
/// @notice function used to set module
/// @param module_ address of the new module
/// @param payloads_ datas to initialize/rebalance on the new module
function setModule(
address module_,
bytes[] calldata payloads_
) external onlyManager nonReentrant {
// store in memory to save gas.
IArrakisLPModule _module = module;
if (address(_module) == module_) revert SameModule();
if (!_whitelistedModules.contains(module_)) {
revert NotWhitelistedModule(module_);
}
module = IArrakisLPModule(module_);
// #region withdraw manager fees balances.
_withdrawManagerBalance(_module);
// #endregion withdraw manager fees balances.
// #region move tokens to the new module.
/// @dev we transfer here all tokens to the new module.
_module.withdraw(module_, BASE);
// #endregion move tokens to the new module.
// #region check if the module is empty.
// #endregion check if the module is empty.
uint256 len = payloads_.length;
for (uint256 i = 0; i < len; i++) {
(bool success,) = module_.call(payloads_[i]);
if (!success) revert CallFailed();
}
emit LogSetModule(module_, payloads_);
}
/// @notice function used to whitelist modules that can used by manager.
/// @param beacons_ array of beacons addresses to use for modules creation.
/// @param data_ array of payload to use for modules creation.
function whitelistModules(
address[] calldata beacons_,
bytes[] calldata data_
) external onlyOwnerCustom {
uint256 len = beacons_.length;
if (len != data_.length) revert ArrayNotSameLength();
address[] memory modules = new address[](len);
for (uint256 i; i < len; i++) {
address _module = IModuleRegistry(moduleRegistry)
.createModule(address(this), beacons_[i], data_[i]);
modules[i] = _module;
_whitelistedModules.add(_module);
}
emit LogWhiteListedModules(modules);
}
/// @notice function used to blacklist modules that can used by manager.
/// @param modules_ array of module addresses to be blacklisted.
function blacklistModules(address[] calldata modules_)
external
onlyOwnerCustom
{
uint256 len = modules_.length;
for (uint256 i; i < len; i++) {
address _module = modules_[i];
if (!_whitelistedModules.contains(_module)) {
revert NotWhitelistedModule(_module);
}
if (address(module) == _module) revert ActiveModule();
_whitelistedModules.remove(_module);
}
emit LogBlackListedModules(modules_);
}
/// @notice function used to get the list of modules whitelisted.
/// @return modules whitelisted modules addresses.
function whitelistedModules()
external
view
returns (address[] memory modules)
{
return _whitelistedModules.values();
}
// #region view functions.
/// @notice function used to get the initial amounts needed to open a position.
/// @return init0 the amount of token0 needed to open a position.
/// @return init1 the amount of token1 needed to open a position.
function getInits()
external
view
returns (uint256 init0, uint256 init1)
{
return module.getInits();
}
/// @notice function used to get the amount of token0 and token1 sitting
/// on the position.
/// @return amount0 the amount of token0 sitting on the position.
/// @return amount1 the amount of token1 sitting on the position.
function totalUnderlying()
public
view
returns (uint256 amount0, uint256 amount1)
{
return module.totalUnderlying();
}
/// @notice function used to get the amounts of token0 and token1 sitting
/// on the position for a specific price.
/// @param priceX96_ price at which we want to simulate our tokens composition
/// @return amount0 the amount of token0 sitting on the position for priceX96.
/// @return amount1 the amount of token1 sitting on the position for priceX96.
function totalUnderlyingAtPrice(uint160 priceX96_)
external
view
returns (uint256 amount0, uint256 amount1)
{
return module.totalUnderlyingAtPrice(priceX96_);
}
// #endregion view functions.
// #region internal functions.
function _withdraw(
address receiver_,
uint256 proportion_
) internal returns (uint256 amount0, uint256 amount1) {
(amount0, amount1) = module.withdraw(receiver_, proportion_);
}
function _withdrawManagerBalance(IArrakisLPModule module_)
internal
returns (uint256 amount0, uint256 amount1)
{
(amount0, amount1) = module_.withdrawManagerBalance();
emit LogWithdrawManagerBalance(amount0, amount1);
}
function _onlyOwnerCheck() internal view virtual;
// #endregion internal functions.
}