1
+ /*
2
+ Copyright 2021 IndexCooperative
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
15
+ */
16
+
17
+ pragma solidity 0.6.10 ;
18
+ pragma experimental "ABIEncoderV2 " ;
19
+
20
+ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
21
+ import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol " ;
22
+
23
+ import { AddressArrayUtils } from "../lib/AddressArrayUtils.sol " ;
24
+ import { BaseAdapter } from "../lib/BaseAdapter.sol " ;
25
+ import { IBaseManager } from "../interfaces/IBaseManager.sol " ;
26
+ import { IGeneralIndexModule } from "../interfaces/IGeneralIndexModule.sol " ;
27
+ import { ISetToken } from "../interfaces/ISetToken.sol " ;
28
+
29
+ /**
30
+ * @title GIMExtension
31
+ * @author Set Protocol
32
+ *
33
+ * Smart contract manager extension that acts as a pass-through contract for interacting with GeneralIndexModule.
34
+ * All functions are only callable by operator. startRebalance() on GIM maps to startRebalanceWithUnits on
35
+ * GIMExtension.
36
+ */
37
+ contract GIMExtension is BaseAdapter {
38
+
39
+ using AddressArrayUtils for address [];
40
+ using SafeMath for uint256 ;
41
+
42
+ /* ============ State Variables ============ */
43
+
44
+ ISetToken public setToken;
45
+ IGeneralIndexModule public generalIndexModule; // GIM
46
+
47
+ /* ============ Constructor ============ */
48
+
49
+ constructor (IBaseManager _manager , IGeneralIndexModule _generalIndexModule ) public BaseAdapter (_manager) {
50
+ generalIndexModule = _generalIndexModule;
51
+ setToken = manager.setToken ();
52
+ }
53
+
54
+ /* ============ External Functions ============ */
55
+
56
+ /**
57
+ * ONLY OPERATOR: Submits a startRebalance call to GeneralIndexModule. Uses internal function so that this contract can be inherited and
58
+ * custom startRebalance logic can be added on top. Components array is sorted in new and old components arrays in order to conform to
59
+ * startRebalance interface. See GIM for function specific restrictions.
60
+ * @param _components Array of components involved in rebalance inclusive of components being removed from set (targetUnit = 0)
61
+ * @param _targetUnits Array of target units at end of rebalance, maps to same index of _components array
62
+ * @param _positionMultiplier Position multiplier when target units were calculated, needed in order to adjust target units if fees accrued
63
+ */
64
+ function startRebalanceWithUnits (
65
+ address [] calldata _components ,
66
+ uint256 [] calldata _targetUnits ,
67
+ uint256 _positionMultiplier
68
+ )
69
+ external
70
+ onlyOperator
71
+ {
72
+ (
73
+ address [] memory newComponents ,
74
+ uint256 [] memory newComponentsTargetUnits ,
75
+ uint256 [] memory oldComponentsTargetUnits
76
+ ) = _sortNewAndOldComponents (_components, _targetUnits);
77
+ _startRebalance (newComponents, newComponentsTargetUnits, oldComponentsTargetUnits, _positionMultiplier);
78
+ }
79
+
80
+ /**
81
+ * ONLY OPERATOR: Submits a setTradeMaximums call to GeneralIndexModule. See GIM for function specific restrictions.
82
+ *
83
+ * @param _components Array of components
84
+ * @param _tradeMaximums Array of trade maximums mapping to correct component
85
+ */
86
+ function setTradeMaximums (
87
+ address [] memory _components ,
88
+ uint256 [] memory _tradeMaximums
89
+ )
90
+ external
91
+ onlyOperator
92
+ {
93
+ bytes memory callData = abi.encodeWithSelector (
94
+ IGeneralIndexModule.setTradeMaximums.selector ,
95
+ setToken,
96
+ _components,
97
+ _tradeMaximums
98
+ );
99
+
100
+ invokeManager (address (generalIndexModule), callData);
101
+ }
102
+
103
+ /**
104
+ * ONLY OPERATOR: Submits a setExchanges call to GeneralIndexModule. See GIM for function specific restrictions.
105
+ *
106
+ * @param _components Array of components
107
+ * @param _exchangeNames Array of exchange names mapping to correct component
108
+ */
109
+ function setExchanges (
110
+ address [] memory _components ,
111
+ string [] memory _exchangeNames
112
+ )
113
+ external
114
+ onlyOperator
115
+ {
116
+ bytes memory callData = abi.encodeWithSelector (
117
+ IGeneralIndexModule.setExchanges.selector ,
118
+ setToken,
119
+ _components,
120
+ _exchangeNames
121
+ );
122
+
123
+ invokeManager (address (generalIndexModule), callData);
124
+ }
125
+
126
+ /**
127
+ * ONLY OPERATOR: Submits a setCoolOffPeriods call to GeneralIndexModule. See GIM for function specific restrictions.
128
+ *
129
+ * @param _components Array of components
130
+ * @param _coolOffPeriods Array of cool off periods to correct component
131
+ */
132
+ function setCoolOffPeriods (
133
+ address [] memory _components ,
134
+ uint256 [] memory _coolOffPeriods
135
+ )
136
+ external
137
+ onlyOperator
138
+ {
139
+ bytes memory callData = abi.encodeWithSelector (
140
+ IGeneralIndexModule.setCoolOffPeriods.selector ,
141
+ setToken,
142
+ _components,
143
+ _coolOffPeriods
144
+ );
145
+
146
+ invokeManager (address (generalIndexModule), callData);
147
+ }
148
+
149
+ /**
150
+ * ONLY OPERATOR: Submits a setExchangeData call to GeneralIndexModule. See GIM for function specific restrictions.
151
+ *
152
+ * @param _components Array of components
153
+ * @param _exchangeData Array of exchange specific arbitrary bytes data
154
+ */
155
+ function setExchangeData (
156
+ address [] memory _components ,
157
+ bytes [] memory _exchangeData
158
+ )
159
+ external
160
+ onlyOperator
161
+ {
162
+ bytes memory callData = abi.encodeWithSelector (
163
+ IGeneralIndexModule.setExchangeData.selector ,
164
+ setToken,
165
+ _components,
166
+ _exchangeData
167
+ );
168
+
169
+ invokeManager (address (generalIndexModule), callData);
170
+ }
171
+
172
+ /**
173
+ * ONLY OPERATOR: Submits a setRaiseTargetPercentage call to GeneralIndexModule. See GIM for function specific restrictions.
174
+ *
175
+ * @param _raiseTargetPercentage Amount to raise all component's unit targets by (in precise units)
176
+ */
177
+ function setRaiseTargetPercentage (uint256 _raiseTargetPercentage ) external onlyOperator {
178
+ bytes memory callData = abi.encodeWithSelector (
179
+ IGeneralIndexModule.setRaiseTargetPercentage.selector ,
180
+ setToken,
181
+ _raiseTargetPercentage
182
+ );
183
+
184
+ invokeManager (address (generalIndexModule), callData);
185
+ }
186
+
187
+ /**
188
+ * ONLY OPERATOR: Submits a setTraderStatus call to GeneralIndexModule. See GIM for function specific restrictions.
189
+ *
190
+ * @param _traders Array trader addresses to toggle status
191
+ * @param _statuses Booleans indicating if matching trader can trade
192
+ */
193
+ function setTraderStatus (
194
+ address [] memory _traders ,
195
+ bool [] memory _statuses
196
+ )
197
+ external
198
+ onlyOperator
199
+ {
200
+ bytes memory callData = abi.encodeWithSelector (
201
+ IGeneralIndexModule.setTraderStatus.selector ,
202
+ setToken,
203
+ _traders,
204
+ _statuses
205
+ );
206
+
207
+ invokeManager (address (generalIndexModule), callData);
208
+ }
209
+
210
+ /**
211
+ * ONLY OPERATOR: Submits a setAnyoneTrade call to GeneralIndexModule. See GIM for function specific restrictions.
212
+ *
213
+ * @param _status Boolean indicating if anyone can call trade
214
+ */
215
+ function setAnyoneTrade (bool _status ) external onlyOperator {
216
+ bytes memory callData = abi.encodeWithSelector (
217
+ IGeneralIndexModule.setAnyoneTrade.selector ,
218
+ setToken,
219
+ _status
220
+ );
221
+
222
+ invokeManager (address (generalIndexModule), callData);
223
+ }
224
+
225
+ /**
226
+ * ONLY OPERATOR: Submits a initialize call to GeneralIndexModule. See GIM for function specific restrictions.
227
+ */
228
+ function initialize () external onlyOperator {
229
+ bytes memory callData = abi.encodeWithSelector (
230
+ IGeneralIndexModule.initialize.selector ,
231
+ setToken
232
+ );
233
+
234
+ invokeManager (address (generalIndexModule), callData);
235
+ }
236
+
237
+ /* ============ Internal Functions ============ */
238
+
239
+ /**
240
+ * Internal function that creates calldata and submits startRebalance call to GeneralIndexModule.
241
+ *
242
+ * @param _newComponents Array of new components to add to allocation
243
+ * @param _newComponentsTargetUnits Array of target units at end of rebalance for new components, maps to same index of _newComponents array
244
+ * @param _oldComponentsTargetUnits Array of target units at end of rebalance for old component, maps to same index of
245
+ * _setToken.getComponents() array, if component being removed set to 0.
246
+ * @param _positionMultiplier Position multiplier when target units were calculated, needed in order to adjust target units
247
+ * if fees accrued
248
+ */
249
+ function _startRebalance (
250
+ address [] memory _newComponents ,
251
+ uint256 [] memory _newComponentsTargetUnits ,
252
+ uint256 [] memory _oldComponentsTargetUnits ,
253
+ uint256 _positionMultiplier
254
+ )
255
+ internal
256
+ {
257
+ bytes memory callData = abi.encodeWithSelector (
258
+ IGeneralIndexModule.startRebalance.selector ,
259
+ setToken,
260
+ _newComponents,
261
+ _newComponentsTargetUnits,
262
+ _oldComponentsTargetUnits,
263
+ _positionMultiplier
264
+ );
265
+
266
+ invokeManager (address (generalIndexModule), callData);
267
+ }
268
+
269
+ /**
270
+ * Internal function that sorts components into old and new components and builds the requisite target unit arrays. Old components target units
271
+ * MUST maintain the order of the components array on the SetToken. The _components array MUST contain an entry for all current components even if
272
+ * component is being removed (targetUnit = 0). This is validated implicitly by calculating the amount of new components that would be added as
273
+ * implied by the array lengths, if more than the expected amount of new components are added then it implies an old component is missing.
274
+ *
275
+ * @param _components Array of components involved in rebalance inclusive of components being removed from set (targetUnit = 0)
276
+ * @param _targetUnits Array of target units at end of rebalance, maps to same index of _components array
277
+ */
278
+ function _sortNewAndOldComponents (
279
+ address [] memory _components ,
280
+ uint256 [] memory _targetUnits
281
+ )
282
+ internal
283
+ view
284
+ returns (address [] memory , uint256 [] memory , uint256 [] memory )
285
+ {
286
+ address [] memory currentComponents = setToken.getComponents ();
287
+
288
+ uint256 currentSetComponentsLength = currentComponents.length ;
289
+ uint256 rebalanceComponentsLength = _components.length ;
290
+
291
+ require (rebalanceComponentsLength >= currentSetComponentsLength, "Components array must be equal or longer than current components " );
292
+
293
+ // We assume that there is an entry for each old component regardless of if it's 0, so any additional components in the array
294
+ // must be added as a new component. Hence we can declare the length of the new components array as the difference between
295
+ // rebalanceComponentsLength and currentSetComponentsLength
296
+ uint256 [] memory oldComponentsTargetUnits = new uint256 [](currentSetComponentsLength);
297
+ address [] memory newComponents = new address [](rebalanceComponentsLength.sub (currentSetComponentsLength));
298
+ uint256 [] memory newTargetUnits = new uint256 [](rebalanceComponentsLength.sub (currentSetComponentsLength));
299
+
300
+ uint256 newCounter; // Count amount of components added to newComponents array to add new components to next index
301
+ for (uint256 i = 0 ; i < rebalanceComponentsLength; i++ ) {
302
+ address component = _components[i];
303
+ (uint256 index , bool isIn ) = currentComponents.indexOf (component);
304
+
305
+ if (isIn) {
306
+ oldComponentsTargetUnits[index] = _targetUnits[i]; // Use index in order to map to correct component in currentComponents array
307
+ } else {
308
+ require (newCounter < newComponents.length , "Unexpected new component added " );
309
+ newComponents[newCounter] = component;
310
+ newTargetUnits[newCounter] = _targetUnits[i];
311
+ newCounter = newCounter.add (1 );
312
+ }
313
+ }
314
+
315
+ return (newComponents, newTargetUnits, oldComponentsTargetUnits);
316
+ }
317
+ }
0 commit comments