Skip to content

Commit c7a0929

Browse files
authored
GIMAdapter implementation and some tests. (#62)
GIMExtension implementation
1 parent 33f2598 commit c7a0929

File tree

12 files changed

+2971
-10
lines changed

12 files changed

+2971
-10
lines changed
Lines changed: 317 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,317 @@
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+
}

contracts/adapters/GovernanceAdapter.sol

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,7 @@ contract GovernanceAdapter is BaseAdapter {
5151
* is part of BaseAdapter.
5252
*
5353
* @param _governanceName Name of governance adapter being used
54-
55-
*/
54+
*/
5655
function delegate(
5756
string memory _governanceName,
5857
address _delegatee

0 commit comments

Comments
 (0)