1
+ /*
2
+ Copyright 2024 Index Cooperative.
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
+ SPDX-License-Identifier: Apache License, Version 2.0
17
+ */
18
+
19
+ pragma solidity 0.6.10 ;
20
+ pragma experimental ABIEncoderV2;
21
+
22
+ import { Address } from "@openzeppelin/contracts/utils/Address.sol " ;
23
+ import { SafeCast } from "@openzeppelin/contracts/utils/SafeCast.sol " ;
24
+
25
+ import { BaseExtension } from "../lib/BaseExtension.sol " ;
26
+ import { IBaseManager } from "../interfaces/IBaseManager.sol " ;
27
+ import { ISetToken } from "../interfaces/ISetToken.sol " ;
28
+ import { IAirdropModule } from "../interfaces/IAirdropModule.sol " ;
29
+ import { ITradeModule } from "../interfaces/ITradeModule.sol " ;
30
+ import { IWrapModuleV2 } from "../interfaces/IWrapModuleV2.sol " ;
31
+
32
+ /**
33
+ * @title ReinvestmentExtensionV1
34
+ * @author Index Cooperative
35
+ */
36
+ contract ReinvestmentExtensionV1 is BaseExtension {
37
+
38
+ using Address for address ;
39
+ using SafeCast for int256 ;
40
+
41
+ /* ========== Structs ================= */
42
+
43
+ struct ExchangeSettings {
44
+ string exchangeName;
45
+ bytes exchangeCallData;
46
+ }
47
+
48
+ /* ========== State Variables ========= */
49
+
50
+ address public immutable WETH;
51
+ ISetToken public immutable setToken;
52
+ IAirdropModule public immutable airdropModule;
53
+ ITradeModule public immutable tradeModule;
54
+ IWrapModuleV2 public immutable wrapModule;
55
+
56
+ mapping (address => ExchangeSettings) public exchangeSettings;
57
+ mapping (address => mapping (address => bool )) public approvedWrapPairs;
58
+
59
+ /* ============ Constructor ============ */
60
+
61
+ constructor (
62
+ IBaseManager _manager ,
63
+ address _weth ,
64
+ IAirdropModule _airdropModule ,
65
+ ITradeModule _tradeModule ,
66
+ IWrapModuleV2 _wrapModule ,
67
+ address [] memory _initialRewardTokens ,
68
+ ExchangeSettings[] memory _initialExchangeSettings ,
69
+ address [][] memory _initialWrapPairs
70
+ ) public BaseExtension (_manager) {
71
+ require (_weth != address (0 ), "Invalid WETH address " );
72
+
73
+ setToken = _manager.setToken ();
74
+ WETH = _weth;
75
+ airdropModule = _airdropModule;
76
+ tradeModule = _tradeModule;
77
+ wrapModule = _wrapModule;
78
+
79
+ require (_initialRewardTokens.length == _initialExchangeSettings.length , "Arrays length mismatch " );
80
+
81
+ for (uint256 i = 0 ; i < _initialRewardTokens.length ; i++ ) {
82
+ require (_initialRewardTokens[i] != address (0 ), "Invalid reward token " );
83
+ exchangeSettings[_initialRewardTokens[i]] = _initialExchangeSettings[i];
84
+ }
85
+
86
+ for (uint256 i = 0 ; i < _initialWrapPairs.length ; i++ ) {
87
+ address underlyingToken = _initialWrapPairs[i][0 ];
88
+ address wrappedToken = _initialWrapPairs[i][1 ];
89
+ require (underlyingToken != address (0 ) && wrappedToken != address (0 ), "Invalid token address " );
90
+ approvedWrapPairs[underlyingToken][wrappedToken] = true ;
91
+ }
92
+ }
93
+
94
+ /* ============ External Functions ============ */
95
+
96
+ /**
97
+ * APPROVED_CALLER ONLY: Absorbs airdropped tokens and trades them for WETH
98
+ *
99
+ * @param _rewardToken Address of reward token to reinvest
100
+ * @param _minReceiveQuantity Minimum amount of WETH to receive
101
+ */
102
+ function reinvest (
103
+ address _rewardToken ,
104
+ uint256 _minReceiveQuantity
105
+ ) external onlyAllowedCaller (msg .sender ) {
106
+ bytes memory absorbCallData = abi.encodeWithSelector (
107
+ IAirdropModule.absorb.selector ,
108
+ setToken,
109
+ _rewardToken
110
+ );
111
+ invokeManager (address (airdropModule), absorbCallData);
112
+
113
+ uint256 rewardUnits = uint256 (setToken.getTotalComponentRealUnits (_rewardToken));
114
+ require (rewardUnits > 0 , "Reward units must be greater than zero " );
115
+ bytes memory tradeCallData = abi.encodeWithSelector (
116
+ ITradeModule.trade.selector ,
117
+ setToken,
118
+ exchangeSettings[_rewardToken].exchangeName,
119
+ _rewardToken,
120
+ rewardUnits,
121
+ WETH,
122
+ _minReceiveQuantity,
123
+ exchangeSettings[_rewardToken].exchangeCallData
124
+ );
125
+ invokeManager (address (tradeModule), tradeCallData);
126
+ }
127
+
128
+ /**
129
+ * OPERATOR ONLY: Wraps underlying token into target wrapped token
130
+ *
131
+ * @param _underlyingToken Address of underlying token
132
+ * @param _wrappedToken Address of wrapped token
133
+ * @param _underlyingUnits Units of underlying token to wrap
134
+ * @param _integrationName Name of wrap module integration
135
+ * @param _wrapData Encoded wrap data
136
+ */
137
+ function wrap (
138
+ address _underlyingToken ,
139
+ address _wrappedToken ,
140
+ uint256 _underlyingUnits ,
141
+ string calldata _integrationName ,
142
+ bytes memory _wrapData
143
+ ) external onlyAllowedCaller (msg .sender ) {
144
+ require (_underlyingUnits > 0 , "Invalid units " );
145
+ require (approvedWrapPairs[_underlyingToken][_wrappedToken], "Unapproved wrap pair " );
146
+
147
+ bytes memory wrapCallData = abi.encodeWithSelector (
148
+ wrapModule.wrap.selector ,
149
+ setToken,
150
+ _underlyingToken,
151
+ _wrappedToken,
152
+ _underlyingUnits,
153
+ _integrationName,
154
+ _wrapData
155
+ );
156
+ invokeManager (address (wrapModule), wrapCallData);
157
+ }
158
+
159
+ /**
160
+ * OPERATOR ONLY: Adds new token to airdrop list
161
+ *
162
+ * @param _token Address of token to add to airdrop list
163
+ */
164
+ function addAirdrop (address _token ) external onlyOperator {
165
+ invokeManager (
166
+ address (airdropModule),
167
+ abi.encodeWithSignature ("addAirdrop(address,address) " , setToken, _token)
168
+ );
169
+ }
170
+
171
+ /**
172
+ * OPERATOR ONLY: Updates exchange settings for a reward token
173
+ */
174
+ function updateExchangeSettings (
175
+ address _rewardToken ,
176
+ ExchangeSettings memory _settings
177
+ ) external onlyOperator {
178
+ require (_rewardToken != address (0 ), "Invalid reward token " );
179
+ exchangeSettings[_rewardToken] = _settings;
180
+ }
181
+
182
+ /**
183
+ * OPERATOR ONLY: Adds an approved wrap pair
184
+ *
185
+ * @param _underlyingToken Address of underlying token
186
+ * @param _wrappedToken Address of wrapped token
187
+ */
188
+ function addWrapPair (address _underlyingToken , address _wrappedToken ) external onlyOperator {
189
+ require (_underlyingToken != address (0 ) && _wrappedToken != address (0 ), "Invalid token address " );
190
+ require (! approvedWrapPairs[_underlyingToken][_wrappedToken], "Pair already exists " );
191
+ approvedWrapPairs[_underlyingToken][_wrappedToken] = true ;
192
+ }
193
+
194
+ /**
195
+ * OPERATOR ONLY: Removes an approved wrap pair
196
+ *
197
+ * @param _underlyingToken Address of underlying token
198
+ * @param _wrappedToken Address of wrapped token
199
+ */
200
+ function removeWrapPair (address _underlyingToken , address _wrappedToken ) external onlyOperator {
201
+ require (_underlyingToken != address (0 ) && _wrappedToken != address (0 ), "Invalid token address " );
202
+ require (approvedWrapPairs[_underlyingToken][_wrappedToken], "Pair does not exist " );
203
+ delete approvedWrapPairs[_underlyingToken][_wrappedToken];
204
+ }
205
+
206
+ /**
207
+ * OPERATOR ONLY: Initializes the AirdropModule
208
+ *
209
+ * @param _airdropSettings Airdrop module initialization settings
210
+ */
211
+ function initializeAirdropModule (IAirdropModule.AirdropSettings memory _airdropSettings ) external onlyOperator {
212
+ bytes memory callData = abi.encodeWithSelector (
213
+ airdropModule.initialize.selector ,
214
+ setToken,
215
+ _airdropSettings
216
+ );
217
+ invokeManager (address (airdropModule), callData);
218
+ }
219
+
220
+ /**
221
+ * OPERATOR ONLY: Initializes the TradeModule
222
+ */
223
+ function initializeTradeModule () external onlyOperator {
224
+ bytes memory tradeModuleData = abi.encodeWithSelector (tradeModule.initialize.selector , setToken);
225
+ invokeManager (address (tradeModule), tradeModuleData);
226
+ }
227
+
228
+ /**
229
+ * OPERATOR ONLY: Initializes the WrapModule
230
+ */
231
+ function initializeWrapModule () external onlyOperator {
232
+ bytes memory wrapModuleData = abi.encodeWithSelector (wrapModule.initialize.selector , setToken);
233
+ invokeManager (address (wrapModule), wrapModuleData);
234
+ }
235
+ }
0 commit comments