generated from yearn/brownie-strategy-mix
-
Notifications
You must be signed in to change notification settings - Fork 70
/
GenericCompound.sol
183 lines (143 loc) · 6.03 KB
/
GenericCompound.sol
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.6.12;
pragma experimental ABIEncoderV2;
import "../Interfaces/Compound/CErc20I.sol";
import "../Interfaces/Compound/InterestRateModel.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/Address.sol";
import "@openzeppelin/contracts/token/ERC20/SafeERC20.sol";
import "../Interfaces/UniswapInterfaces/IUniswapV2Router02.sol";
import "./GenericLenderBase.sol";
/********************
* A lender plugin for LenderYieldOptimiser for any erc20 asset on compound (not eth)
* Made by SamPriestley.com
* https://github.com/Grandthrax/yearnv2/blob/master/contracts/GenericDyDx/GenericCompound.sol
*
********************* */
contract GenericCompound is GenericLenderBase {
using SafeERC20 for IERC20;
using Address for address;
using SafeMath for uint256;
uint256 private constant blocksPerYear = 2_300_000;
address public constant uniswapRouter = address(0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D);
address public constant comp = address(0xc00e94Cb662C3520282E6f5717214004A7f26888);
address public constant weth = address(0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2);
uint256 public minCompToSell = 0.5 ether;
CErc20I public cToken;
constructor(
address _strategy,
string memory name,
address _cToken
) public GenericLenderBase(_strategy, name) {
cToken = CErc20I(_cToken);
require(cToken.underlying() == address(want), "WRONG CTOKEN");
want.approve(_cToken, uint256(-1));
}
function nav() external view override returns (uint256) {
return _nav();
}
function _nav() internal view returns (uint256) {
return want.balanceOf(address(this)).add(underlyingBalanceStored());
}
function underlyingBalanceStored() public view returns (uint256 balance) {
uint256 currentCr = cToken.balanceOf(address(this));
if (currentCr == 0) {
balance = 0;
} else {
balance = currentCr.mul(cToken.exchangeRateStored()).div(1e18);
}
}
function apr() external view override returns (uint256) {
return _apr();
}
function _apr() internal view returns (uint256) {
return cToken.supplyRatePerBlock().mul(blocksPerYear);
}
function weightedApr() external view override returns (uint256) {
uint256 a = _apr();
return a.mul(_nav());
}
function withdraw(uint256 amount) external override management returns (uint256) {
return _withdraw(amount);
}
//emergency withdraw. sends balance plus amount to governance
function emergencyWithdraw(uint256 amount) external override management {
cToken.redeemUnderlying(amount);
want.safeTransfer(vault.governance(), want.balanceOf(address(this)));
}
//withdraw an amount including any want balance
function _withdraw(uint256 amount) internal returns (uint256) {
uint256 balanceUnderlying = cToken.balanceOfUnderlying(address(this));
uint256 looseBalance = want.balanceOf(address(this));
uint256 total = balanceUnderlying.add(looseBalance);
if (amount > total) {
//cant withdraw more than we own
amount = total;
}
if (looseBalance >= amount) {
want.safeTransfer(address(strategy), amount);
return amount;
}
//not state changing but OK because of previous call
uint256 liquidity = want.balanceOf(address(cToken));
if (liquidity > 1) {
uint256 toWithdraw = amount.sub(looseBalance);
if (toWithdraw <= liquidity) {
//we can take all
cToken.redeemUnderlying(toWithdraw);
} else {
//take all we can
cToken.redeemUnderlying(liquidity);
}
}
_disposeOfComp();
looseBalance = want.balanceOf(address(this));
want.safeTransfer(address(strategy), looseBalance);
return looseBalance;
}
function _disposeOfComp() internal {
uint256 _comp = IERC20(comp).balanceOf(address(this));
if (_comp > minCompToSell) {
address[] memory path = new address[](3);
path[0] = comp;
path[1] = weth;
path[2] = address(want);
IUniswapV2Router02(uniswapRouter).swapExactTokensForTokens(_comp, uint256(0), path, address(this), now);
}
}
function deposit() external override management {
uint256 balance = want.balanceOf(address(this));
cToken.mint(balance);
}
function withdrawAll() external override management returns (bool) {
uint256 invested = _nav();
uint256 returned = _withdraw(invested);
return returned >= invested;
}
//think about this
function enabled() external view override returns (bool) {
return true;
}
function hasAssets() external view override returns (bool) {
//return cToken.balanceOf(address(this)) > 0;
return cToken.balanceOf(address(this)) > 0 || want.balanceOf(address(this)) > 0;
}
function aprAfterDeposit(uint256 amount) external view override returns (uint256) {
uint256 cashPrior = want.balanceOf(address(cToken));
uint256 borrows = cToken.totalBorrows();
uint256 reserves = cToken.totalReserves();
uint256 reserverFactor = cToken.reserveFactorMantissa();
InterestRateModel model = cToken.interestRateModel();
//the supply rate is derived from the borrow rate, reserve factor and the amount of total borrows.
uint256 supplyRate = model.getSupplyRate(cashPrior.add(amount), borrows, reserves, reserverFactor);
return supplyRate.mul(blocksPerYear);
}
function protectedTokens() internal view override returns (address[] memory) {
address[] memory protected = new address[](3);
protected[0] = address(want);
protected[1] = address(cToken);
protected[2] = comp;
return protected;
}
}