Skip to content

Commit 1e60264

Browse files
authored
Add index-coop contracts needed for Perp Strategy Extension deployment (#7)
* Remove BaseManagerV2 * Remove StreamingFeeSplitExtension * Add FeeSplitExtension contract * Add SupplyCapIssuanceHook
1 parent f2934bd commit 1e60264

17 files changed

+743
-2743
lines changed

contracts/extensions/StreamingFeeSplitExtension.sol renamed to contracts/extensions/FeeSplitExtension.sol

Lines changed: 54 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -23,22 +23,21 @@ import { Address } from "@openzeppelin/contracts/utils/Address.sol";
2323
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
2424

2525
import { BaseExtension } from "../lib/BaseExtension.sol";
26+
import { IIssuanceModule } from "../interfaces/IIssuanceModule.sol";
2627
import { IBaseManager } from "../interfaces/IBaseManager.sol";
2728
import { ISetToken } from "../interfaces/ISetToken.sol";
2829
import { IStreamingFeeModule } from "../interfaces/IStreamingFeeModule.sol";
2930
import { PreciseUnitMath } from "../lib/PreciseUnitMath.sol";
3031
import { TimeLockUpgrade } from "../lib/TimeLockUpgrade.sol";
31-
import { MutualUpgrade } from "../lib/MutualUpgrade.sol";
3232

3333

3434
/**
35-
* @title StreamingFeeSplitExtension
35+
* @title FeeSplitExtension
3636
* @author Set Protocol
3737
*
38-
* Smart contract manager extension that allows for splitting and setting streaming fees. Fee splits are updated by operator.
39-
* Any fee updates are timelocked.
38+
* Smart contract extension that allows for splitting and setting streaming and mint/redeem fees.
4039
*/
41-
contract StreamingFeeSplitExtension is BaseExtension, TimeLockUpgrade, MutualUpgrade {
40+
contract FeeSplitExtension is BaseExtension, TimeLockUpgrade {
4241
using Address for address;
4342
using PreciseUnitMath for uint256;
4443
using SafeMath for uint256;
@@ -56,6 +55,7 @@ contract StreamingFeeSplitExtension is BaseExtension, TimeLockUpgrade, MutualUpg
5655

5756
ISetToken public setToken;
5857
IStreamingFeeModule public streamingFeeModule;
58+
IIssuanceModule public issuanceModule;
5959

6060
// Percent of fees in precise units (10^16 = 1%) sent to operator, rest to methodologist
6161
uint256 public operatorFeeSplit;
@@ -68,13 +68,15 @@ contract StreamingFeeSplitExtension is BaseExtension, TimeLockUpgrade, MutualUpg
6868
constructor(
6969
IBaseManager _manager,
7070
IStreamingFeeModule _streamingFeeModule,
71+
IIssuanceModule _issuanceModule,
7172
uint256 _operatorFeeSplit,
7273
address _operatorFeeRecipient
7374
)
7475
public
7576
BaseExtension(_manager)
7677
{
7778
streamingFeeModule = _streamingFeeModule;
79+
issuanceModule = _issuanceModule;
7880
operatorFeeSplit = _operatorFeeSplit;
7981
operatorFeeRecipient = _operatorFeeRecipient;
8082
setToken = manager.setToken();
@@ -83,9 +85,10 @@ contract StreamingFeeSplitExtension is BaseExtension, TimeLockUpgrade, MutualUpg
8385
/* ============ External Functions ============ */
8486

8587
/**
86-
* ANYONE CALLABLE: Accrues fees from streaming fee module. Gets resulting balance after fee accrual,
87-
* calculates fees for operator and methodologist, and sends to operatorFeeRecipient and methodologist
88-
* respectively.
88+
* ANYONE CALLABLE: Accrues fees from streaming fee module. Gets resulting balance after fee accrual, calculates fees for
89+
* operator and methodologist, and sends to operator fee recipient and methodologist respectively. NOTE: mint/redeem fees
90+
* will automatically be sent to this address so reading the balance of the SetToken in the contract after accrual is
91+
* sufficient for accounting for all collected fees.
8992
*/
9093
function accrueFeesAndDistribute() public {
9194
// Emits a FeeActualized event
@@ -110,97 +113,91 @@ contract StreamingFeeSplitExtension is BaseExtension, TimeLockUpgrade, MutualUpg
110113
}
111114

112115
/**
113-
* MUTUAL UPGRADE: Initializes the streaming fee module. Operator and Methodologist must each call
114-
* this function to execute the update.
116+
* ONLY OPERATOR: Updates streaming fee on StreamingFeeModule.
117+
* Because the method is timelocked, each party must call it twice: once to set the lock and once to execute.
115118
*
116-
* This method is called after invoking `replaceProtectedModule` or `emergencyReplaceProtectedModule`
117-
* to configure the replacement streaming fee module's fee settings.
119+
* Method is timelocked to protect token owners from sudden changes in fee structure which
120+
* they would rather not bear. The delay gives them a chance to exit their positions without penalty.
121+
*
122+
* NOTE: This will accrue streaming fees though not send to operator fee recipient and methodologist.
118123
*
119-
* @dev FeeState settings encode the following struct
120-
* ```
121-
* struct FeeState {
122-
* address feeRecipient; // Address to accrue fees to
123-
* uint256 maxStreamingFeePercentage; // Max streaming fee maanager commits to using (1% = 1e16, 100% = 1e18)
124-
* uint256 streamingFeePercentage; // Percent of Set accruing to manager annually (1% = 1e16, 100% = 1e18)
125-
* uint256 lastStreamingFeeTimestamp; // Timestamp last streaming fee was accrued
126-
*}
127-
*```
128-
* @param _settings FeeModule.FeeState settings
124+
* @param _newFee Percent of Set accruing to fee extension annually (1% = 1e16, 100% = 1e18)
129125
*/
130-
function initializeModule(IStreamingFeeModule.FeeState memory _settings)
126+
function updateStreamingFee(uint256 _newFee)
131127
external
132-
mutualUpgrade(manager.operator(), manager.methodologist())
128+
onlyOperator
129+
timeLockUpgrade
133130
{
134-
bytes memory callData = abi.encodeWithSelector(
135-
IStreamingFeeModule.initialize.selector,
136-
manager.setToken(),
137-
_settings
138-
);
139-
131+
bytes memory callData = abi.encodeWithSignature("updateStreamingFee(address,uint256)", manager.setToken(), _newFee);
140132
invokeManager(address(streamingFeeModule), callData);
141133
}
142134

143135
/**
144-
* MUTUAL UPGRADE: Updates streaming fee on StreamingFeeModule. Operator and Methodologist must
145-
* each call this function to execute the update. Because the method is timelocked, each party
146-
* must call it twice: once to set the lock and once to execute.
136+
* ONLY OPERATOR: Updates issue fee on IssuanceModule. Only is executed once time lock has passed.
137+
* Because the method is timelocked, each party must call it twice: once to set the lock and once to execute.
147138
*
148139
* Method is timelocked to protect token owners from sudden changes in fee structure which
149140
* they would rather not bear. The delay gives them a chance to exit their positions without penalty.
150141
*
151-
* NOTE: This will accrue streaming fees though not send to operator fee recipient and methodologist.
152-
*
153-
* @param _newFee Percent of Set accruing to fee extension annually (1% = 1e16, 100% = 1e18)
142+
* @param _newFee New issue fee percentage in precise units (1% = 1e16, 100% = 1e18)
154143
*/
155-
function updateStreamingFee(uint256 _newFee)
144+
function updateIssueFee(uint256 _newFee)
156145
external
157-
mutualUpgrade(manager.operator(), manager.methodologist())
146+
onlyOperator
158147
timeLockUpgrade
159148
{
160-
bytes memory callData = abi.encodeWithSelector(
161-
IStreamingFeeModule.updateStreamingFee.selector,
162-
manager.setToken(),
163-
_newFee
164-
);
149+
bytes memory callData = abi.encodeWithSignature("updateIssueFee(address,uint256)", manager.setToken(), _newFee);
150+
invokeManager(address(issuanceModule), callData);
151+
}
165152

166-
invokeManager(address(streamingFeeModule), callData);
153+
/**
154+
* ONLY OPERATOR: Updates redeem fee on IssuanceModule. Only is executed once time lock has passed.
155+
* Because the method is timelocked, each party must call it twice: once to set the lock and once to execute.
156+
*
157+
* Method is timelocked to protect token owners from sudden changes in fee structure which
158+
* they would rather not bear. The delay gives them a chance to exit their positions without penalty.
159+
*
160+
* @param _newFee New redeem fee percentage in precise units (1% = 1e16, 100% = 1e18)
161+
*/
162+
function updateRedeemFee(uint256 _newFee)
163+
external
164+
onlyOperator
165+
timeLockUpgrade
166+
{
167+
bytes memory callData = abi.encodeWithSignature("updateRedeemFee(address,uint256)", manager.setToken(), _newFee);
168+
invokeManager(address(issuanceModule), callData);
167169
}
168170

169171
/**
170-
* MUTUAL UPGRADE: Updates fee recipient on streaming fee module.
172+
* ONLY OPERATOR: Updates fee recipient on both streaming fee and issuance modules.
171173
*
172174
* @param _newFeeRecipient Address of new fee recipient. This should be the address of the fee extension itself.
173175
*/
174176
function updateFeeRecipient(address _newFeeRecipient)
175177
external
176-
mutualUpgrade(manager.operator(), manager.methodologist())
178+
onlyOperator
177179
{
178-
bytes memory callData = abi.encodeWithSelector(
179-
IStreamingFeeModule.updateFeeRecipient.selector,
180-
manager.setToken(),
181-
_newFeeRecipient
182-
);
183-
180+
bytes memory callData = abi.encodeWithSignature("updateFeeRecipient(address,address)", manager.setToken(), _newFeeRecipient);
184181
invokeManager(address(streamingFeeModule), callData);
182+
invokeManager(address(issuanceModule), callData);
185183
}
186184

187185
/**
188-
* MUTUAL UPGRADE: Updates fee split between operator and methodologist. Split defined in precise units (1% = 10^16).
189-
* Fees will be accrued and distributed before the new split goes into effect.
186+
* ONLY OPERATOR: Updates fee split between operator and methodologist. Split defined in precise units (1% = 10^16).
190187
*
191188
* @param _newFeeSplit Percent of fees in precise units (10^16 = 1%) sent to operator, (rest go to the methodologist).
192189
*/
193190
function updateFeeSplit(uint256 _newFeeSplit)
194191
external
195-
mutualUpgrade(manager.operator(), manager.methodologist())
192+
onlyOperator
196193
{
197194
require(_newFeeSplit <= PreciseUnitMath.preciseUnit(), "Fee must be less than 100%");
198195
accrueFeesAndDistribute();
199196
operatorFeeSplit = _newFeeSplit;
200197
}
201198

202199
/**
203-
* OPERATOR ONLY: Updates the address that receives the operator's share of the fees (see IIP-72)
200+
* ONLY OPERATOR: Updates the address that receives the operator's fees (see IIP-72)
204201
*
205202
* @param _newOperatorFeeRecipient Address to send operator's fees to.
206203
*/
@@ -211,4 +208,4 @@ contract StreamingFeeSplitExtension is BaseExtension, TimeLockUpgrade, MutualUpg
211208
require(_newOperatorFeeRecipient != address(0), "Zero address not valid");
212209
operatorFeeRecipient = _newOperatorFeeRecipient;
213210
}
214-
}
211+
}
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
/*
2+
Copyright 2021 Set Labs Inc.
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 { Ownable } from "@openzeppelin/contracts/access/Ownable.sol";
23+
import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol";
24+
25+
import { IManagerIssuanceHook } from "../interfaces/IManagerIssuanceHook.sol";
26+
import { ISetToken } from "../interfaces/ISetToken.sol";
27+
28+
29+
/**
30+
* @title SupplyCapIssuanceHook
31+
* @author Set Protocol
32+
*
33+
* Issuance hook that checks new issuances won't push SetToken totalSupply over supply cap.
34+
*/
35+
contract SupplyCapIssuanceHook is Ownable, IManagerIssuanceHook {
36+
using SafeMath for uint256;
37+
38+
/* ============ Events ============ */
39+
40+
event SupplyCapUpdated(uint256 _newCap);
41+
42+
/* ============ State Variables ============ */
43+
44+
// Cap on totalSupply of Sets
45+
uint256 public supplyCap;
46+
47+
/* ============ Constructor ============ */
48+
49+
/**
50+
* Constructor, overwrites owner and original supply cap.
51+
*
52+
* @param _initialOwner Owner address, overwrites Ownable logic which sets to deployer as default
53+
* @param _supplyCap Supply cap for Set (in wei of Set)
54+
*/
55+
constructor(
56+
address _initialOwner,
57+
uint256 _supplyCap
58+
)
59+
public
60+
{
61+
supplyCap = _supplyCap;
62+
63+
// Overwrite _owner param of Ownable contract
64+
transferOwnership(_initialOwner);
65+
}
66+
67+
/**
68+
* Adheres to IManagerIssuanceHook interface, and checks to make sure the current issue call won't push total supply over cap.
69+
*/
70+
function invokePreIssueHook(
71+
ISetToken _setToken,
72+
uint256 _issueQuantity,
73+
address /*_sender*/,
74+
address /*_to*/
75+
)
76+
external
77+
override
78+
{
79+
uint256 totalSupply = _setToken.totalSupply();
80+
81+
require(totalSupply.add(_issueQuantity) <= supplyCap, "Supply cap exceeded");
82+
}
83+
84+
/**
85+
* Adheres to IManagerIssuanceHook interface
86+
*/
87+
function invokePreRedeemHook(
88+
ISetToken _setToken,
89+
uint256 _redeemQuantity,
90+
address _sender,
91+
address _to
92+
)
93+
external
94+
override
95+
{}
96+
97+
/**
98+
* ONLY OWNER: Updates supply cap
99+
*/
100+
function updateSupplyCap(uint256 _newCap) external onlyOwner {
101+
supplyCap = _newCap;
102+
SupplyCapUpdated(_newCap);
103+
}
104+
}

contracts/lib/BaseExtension.sol

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -117,16 +117,6 @@ abstract contract BaseExtension {
117117

118118
/* ============ Internal Functions ============ */
119119

120-
/**
121-
* Invoke manager to transfer tokens from manager to other contract.
122-
*
123-
* @param _token Token being transferred from manager contract
124-
* @param _amount Amount of token being transferred
125-
*/
126-
function invokeManagerTransfer(address _token, address _destination, uint256 _amount) internal {
127-
manager.transferTokens(_token, _destination, _amount);
128-
}
129-
130120
/**
131121
* Invoke call from manager
132122
*

0 commit comments

Comments
 (0)