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
+
17
+ pragma solidity 0.6.10 ;
18
+
19
+
20
+ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol " ;
21
+ import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol " ;
22
+ import { SafeMath } from "@openzeppelin/contracts/math/SafeMath.sol " ;
23
+
24
+ import { StakingRewardsV2 } from "../staking/StakingRewardsV2.sol " ;
25
+ import { IMasterChef } from "../interfaces/IMasterChef.sol " ;
26
+ import { IPair } from "../interfaces/IPair.sol " ;
27
+ import { Vesting } from "./Vesting.sol " ;
28
+
29
+
30
+ /**
31
+ * @title IndexPowah
32
+ * @author Set Protocol
33
+ *
34
+ * An ERC20 token used for tracking the voting power for the Index Coop. The mutative functions of
35
+ * the ERC20 interface have been disabled since the token is only designed to count votes for the
36
+ * sake of utilizing Snapshot's erc20-balance-of strategy. This contract is inspired by Sushiswap's
37
+ * SUSHIPOWAH contract which serves the same purpose.
38
+ */
39
+ contract IndexPowah is IERC20 , Ownable {
40
+
41
+ using SafeMath for uint256 ;
42
+
43
+ IERC20 public indexToken;
44
+
45
+ IMasterChef public masterChef;
46
+ uint256 public masterChefId;
47
+ IPair public uniPair;
48
+ IPair public sushiPair;
49
+
50
+ StakingRewardsV2[] public farms;
51
+ Vesting[] public vesting;
52
+
53
+ /**
54
+ * Sets the appropriate state variables for the contract.
55
+ *
56
+ * @param _owner owner of this contract
57
+ * @param _indexToken Index Coop's governance token contract
58
+ * @param _uniPair INDEX-WETH Uniswap pair
59
+ * @param _sushiPair INDEX-WETH Sushiswap pair
60
+ * @param _masterChef Sushiswap MasterChef (Onsen) contract
61
+ * @param _farms array of Index Coop staking farms
62
+ * @param _vesting array of vesting contracts from the index sale and full time contributors
63
+ */
64
+ constructor (
65
+ address _owner ,
66
+ IERC20 _indexToken ,
67
+ IPair _uniPair ,
68
+ IPair _sushiPair ,
69
+ IMasterChef _masterChef ,
70
+ uint256 _masterChefId ,
71
+ StakingRewardsV2[] memory _farms ,
72
+ Vesting[] memory _vesting
73
+ )
74
+ public
75
+ {
76
+ indexToken = _indexToken;
77
+ uniPair = _uniPair;
78
+ sushiPair = _sushiPair;
79
+ masterChef = _masterChef;
80
+ masterChefId = _masterChefId;
81
+ farms = _farms;
82
+ vesting = _vesting;
83
+
84
+ transferOwnership (_owner);
85
+ }
86
+
87
+ /**
88
+ * Computes an address's balance of IndexPowah. Balances can not be transfered in the traditional way,
89
+ * but are instead computed by the amount of index that an account directly hold, or indirectly holds
90
+ * through the staking contracts, vesting contracts, uniswap, and sushiswap.
91
+ *
92
+ * @param _account the address of the voter
93
+ */
94
+ function balanceOf (address _account ) public view override returns (uint256 ) {
95
+ uint256 indexAmount = indexToken.balanceOf (_account);
96
+ uint256 unclaimedInFarms = _getFarmVotes (_account);
97
+ uint256 vestingVotes = _getVestingVotes (_account);
98
+ uint256 dexVotes = _getDexVotes (_account, uniPair) + _getDexVotes (_account, sushiPair) + _getMasterChefVotes (_account);
99
+
100
+ return indexAmount + unclaimedInFarms + vestingVotes + dexVotes;
101
+ }
102
+
103
+ /**
104
+ * ONLY OWNER: Adds new Index farms to be tracked
105
+ *
106
+ * @param _newFarms list of new farms to be tracked
107
+ */
108
+ function addFarms (StakingRewardsV2[] calldata _newFarms ) external onlyOwner {
109
+ for (uint256 i = 0 ; i < _newFarms.length ; i++ ) {
110
+ farms.push (_newFarms[0 ]);
111
+ }
112
+ }
113
+
114
+ /**
115
+ * ONLY OWNER: Adds new Index vesting contracts to be tracked
116
+ *
117
+ * @param _newVesting list of new vesting contracts to be tracked
118
+ */
119
+ function addVesting (Vesting[] calldata _newVesting ) external onlyOwner {
120
+ for (uint256 i = 0 ; i < _newVesting.length ; i++ ) {
121
+ vesting.push (_newVesting[i]);
122
+ }
123
+ }
124
+
125
+ /**
126
+ * ONLY OWNER: Updates the MasterChef contract and pool ID
127
+ *
128
+ * @param _newMasterChef address of the new MasterChef contract
129
+ * @param _newMasterChefId new pool id for the index-eth MasterChef rewards
130
+ */
131
+ function updateMasterChef (IMasterChef _newMasterChef , uint256 _newMasterChefId ) external onlyOwner {
132
+ masterChef = _newMasterChef;
133
+ masterChefId = _newMasterChefId;
134
+ }
135
+
136
+ function _getFarmVotes (address _account ) internal view returns (uint256 ) {
137
+ uint256 sum = 0 ;
138
+ for (uint256 i = 0 ; i < farms.length ; i++ ) {
139
+ sum += farms[i].earned (_account);
140
+ }
141
+ return sum;
142
+ }
143
+
144
+ function _getVestingVotes (address _account ) internal view returns (uint256 ) {
145
+ uint256 sum = 0 ;
146
+ for (uint256 i = 0 ; i < vesting.length ; i++ ) {
147
+ if (vesting[i].recipient () == _account) {
148
+ sum += indexToken.balanceOf (address (vesting[i]));
149
+ }
150
+ }
151
+ return sum;
152
+ }
153
+
154
+ function _getDexVotes (address _account , IPair pair ) internal view returns (uint256 ) {
155
+ uint256 lpBalance = pair.balanceOf (_account);
156
+ return _getDexVotesFromBalance (lpBalance, pair);
157
+ }
158
+
159
+ function _getMasterChefVotes (address _account ) internal view returns (uint256 ) {
160
+ (uint256 lpBalance ,) = masterChef.userInfo (masterChefId, _account);
161
+ return _getDexVotesFromBalance (lpBalance, sushiPair);
162
+ }
163
+
164
+ function _getDexVotesFromBalance (uint256 lpBalance , IPair pair ) internal view returns (uint256 ) {
165
+ uint256 lpIndex = indexToken.balanceOf (address (pair));
166
+ uint256 lpTotal = pair.totalSupply ();
167
+ if (lpTotal == 0 ) return 0 ;
168
+ return lpIndex.mul (lpBalance).div (lpTotal);
169
+ }
170
+
171
+
172
+ /**
173
+ * These functions are not used, but have been left in to keep the token ERC20 compliant
174
+ */
175
+ function name () public pure returns (string memory ) { return "INDEXPOWAH " ; }
176
+ function symbol () public pure returns (string memory ) { return "INDEXPOWAH " ; }
177
+ function decimals () public pure returns (uint8 ) { return 18 ; }
178
+ function totalSupply () public view override returns (uint256 ) { return indexToken.totalSupply (); }
179
+ function allowance (address , address ) public view override returns (uint256 ) { return 0 ; }
180
+ function transfer (address , uint256 ) public override returns (bool ) { return false ; }
181
+ function approve (address , uint256 ) public override returns (bool ) { return false ; }
182
+ function transferFrom (address , address , uint256 ) public override returns (bool ) { return false ; }
183
+ }
0 commit comments