forked from Fankouzu/my-yearn-protocol
-
Notifications
You must be signed in to change notification settings - Fork 0
/
StrategyDForceUSDC.sol
371 lines (320 loc) · 13.3 KB
/
StrategyDForceUSDC.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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
/**
*Submitted for verification at Etherscan.io on 2020-08-13
*/
// SPDX-License-Identifier: MIT
pragma solidity ^0.5.17;
import "./common.sol";
/*
策略必须执行以下调用;
- deposit() 存款
- withdraw(address) 必须排除yield中使用的所有令牌-Controller角色-withdraw应该返回给Controller
- withdraw(uint) - 控制器 | 保管箱角色-提款应始终返回保管库
- withdrawAll() - 控制器 | 保管箱角色-提款应始终返回保管库
- balanceOf() 查询余额
在可能的情况下,策略必须保持尽可能不变,而不是更新变量,我们通过在控制器中链接合同来更新合同
A strategy must implement the following calls;
- deposit()
- withdraw(address) must exclude any tokens used in the yield - Controller role - withdraw should return to Controller
- withdraw(uint) - Controller | Vault role - withdraw should always return to vault
- withdrawAll() - Controller | Vault role - withdraw should always return to vault
- balanceOf()
Where possible, strategies must remain as immutable as possible, instead of updating variables, we update the contract by linking it in the controller
*/
interface dRewards {
function withdraw(uint256) external;
function getReward() external;
function stake(uint256) external;
function balanceOf(address) external view returns (uint256);
function exit() external;
}
interface dERC20 {
function mint(address, uint256) external;
function redeem(address, uint256) external;
function getTokenBalance(address) external view returns (uint256);
function getExchangeRate() external view returns (uint256);
}
interface Uni {
function swapExactTokensForTokens(
uint256,
uint256,
address[] calldata,
address,
uint256
) external;
}
// USDC策略合约 地址:0xA30d1D98C502378ad61Fe71BcDc3a808CF60b897
contract StrategyDForceUSDC {
using SafeERC20 for IERC20;
using Address for address;
using SafeMath for uint256;
address public constant want = address(
0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
); // USDC
address public constant dusdc = address(
0x16c9cF62d8daC4a38FB50Ae5fa5d51E9170F3179
); // dForce: dUSDC Token
address public constant pool = address(
0xB71dEFDd6240c45746EC58314a01dd6D833fD3b5
); // dForce: Unipool
address public constant df = address(
0x431ad2ff6a9C365805eBaD47Ee021148d6f7DBe0
); // dForce: DF Token
address public constant uni = address(
0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D
); // Uniswap V2: Router 2
address public constant weth = address(
0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2
); // used for df <> weth <> usdc route
uint256 public performanceFee = 5000;
uint256 public constant performanceMax = 10000;
uint256 public withdrawalFee = 500;
uint256 public constant withdrawalMax = 10000;
address public governance; // 0xfeb4acf3df3cdea7399794d0869ef76a6efaff52
address public controller; // 0x9e65ad11b299ca0abefc2799ddb6314ef2d91080
address public strategist; // 0x2d407ddb06311396fe14d4b49da5f0471447d45c
/**
* @dev 构造函数
* @param _controller 控制器合约地址
*/
// 0x9e65ad11b299ca0abefc2799ddb6314ef2d91080
constructor(address _controller) public {
governance = msg.sender;
strategist = msg.sender;
controller = _controller;
}
/**
* @dev 设置策略员地址
* @param _strategist 策略员地址
* @notice 只能由治理地址设置
*/
// 0x2D407dDb06311396fE14D4b49da5F0471447d45C
function setStrategist(address _strategist) external {
require(msg.sender == governance, "!governance");
strategist = _strategist;
}
/**
* @dev 设置提款手续费
* @param _withdrawalFee 提款手续费
* @notice 只能由治理地址设置
*/
function setWithdrawalFee(uint256 _withdrawalFee) external {
require(msg.sender == governance, "!governance");
withdrawalFee = _withdrawalFee;
}
/**
* @dev 设置性能费
* @param _performanceFee 性能费
* @notice 只能由治理地址设置
*/
function setPerformanceFee(uint256 _performanceFee) external {
require(msg.sender == governance, "!governance");
performanceFee = _performanceFee;
}
/**
* @dev 存款方法
* @notice 将合约中的USDC发送给dForce: dUSDC Token铸造DToken,再将DToken发送到dForce: Unipool做质押
*/
function deposit() public {
// USDC余额 = 当前合约在USDC合约中的余额
uint256 _want = IERC20(want).balanceOf(address(this));
// 如果USDC余额 > 0
if (_want > 0) {
// 将USDC的余额批准给dForce: dUSDC Token
IERC20(want).safeApprove(dusdc, 0);
IERC20(want).safeApprove(dusdc, _want);
// 在dForce: dUSDC Token铸造数额为USDC余额的DToken
dERC20(dusdc).mint(address(this), _want);
}
// dusdc余额 = 当前合约在dusdc合约中的余额
uint256 _dusdc = IERC20(dusdc).balanceOf(address(this));
// 如果dusdc余额 > 0
if (_dusdc > 0) {
// 将dusdc的余额批准给dForce: Unipool
IERC20(dusdc).safeApprove(pool, 0);
IERC20(dusdc).safeApprove(pool, _dusdc);
// 在dForce: Unipool中质押dusdc的余额
dRewards(pool).stake(_dusdc);
}
}
/**
* @dev 提款方法
* @param _asset 资产地址
* @notice 将当前合约在_asset资产合约的余额发送给控制器合约
*/
// 控制器仅用于从灰尘中产生额外奖励的功能
// Controller only function for creating additional rewards from dust
function withdraw(IERC20 _asset) external returns (uint256 balance) {
// 只能通过控制器合约调用
require(msg.sender == controller, "!controller");
// 资产地址不能等于USDC地址
require(want != address(_asset), "want");
// 资产地址不能等于DToken地址
require(dusdc != address(_asset), "dusdc");
// 当前合约在资产合约中的余额
balance = _asset.balanceOf(address(this));
// 将资产合约的余额发送给控制器合约
_asset.safeTransfer(controller, balance);
}
/**
* @dev 提款方法
* @param _amount 提款数额
* @notice 必须从控制器合约调用,将资产赎回,扣除提款费,将提款费发送给控制器合约的奖励地址,再将剩余发送到保险库
*/
// 提取部分资金,通常用于金库提取
// Withdraw partial funds, normally used with a vault withdrawal
function withdraw(uint256 _amount) external {
// 只能通过控制器合约调用
require(msg.sender == controller, "!controller");
// 当前合约在USDC合约中的余额
uint256 _balance = IERC20(want).balanceOf(address(this));
// 如果USDC余额 < 提款数额
if (_balance < _amount) {
// 数额 = 赎回资产(数额 - 余额)
_amount = _withdrawSome(_amount.sub(_balance));
// 数额 + 余额
_amount = _amount.add(_balance);
}
// 费用 = 数额 * 5%
uint256 _fee = _amount.mul(withdrawalFee).div(withdrawalMax);
// 将费用发送到控制器奖励地址
IERC20(want).safeTransfer(Controller(controller).rewards(), _fee);
// 保险库 = want合约在控制器的保险库地址
address _vault = Controller(controller).vaults(address(want));
// 确认保险库地址不为空
require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds
// 将数额 - 费用 发送到保险库地址
IERC20(want).safeTransfer(_vault, _amount.sub(_fee));
}
/**
* @dev 提款全部方法
* @notice 必须从控制器合约调用,将提出的USDC发送到保险库合约
*/
// 提取所有资金,通常在迁移策略时使用
// Withdraw all funds, normally used when migrating strategies
function withdrawAll() external returns (uint256 balance) {
// 只能通过控制器合约调用
require(msg.sender == controller, "!controller");
// 内部提款全部方法
_withdrawAll();
// 当前合约的USDC余额
balance = IERC20(want).balanceOf(address(this));
// 保险库合约地址
address _vault = Controller(controller).vaults(address(want));
// 确认保险库地址不为空
require(_vault != address(0), "!vault"); // additional protection so we don't burn the funds
// 将当前合约的USDC余额发送到保险库合约
IERC20(want).safeTransfer(_vault, balance);
}
/**
* @dev 内部提款全部方法
* @notice 执行dForce: Unipool的退出方法,提款到当前账户,并获取奖励,然后执行dUSDC的赎回方法到当前合约,换取USDC
*/
function _withdrawAll() internal {
// 执行dForce: Unipool的退出方法,提款到当前账户,并获取奖励
dRewards(pool).exit();
// 当前合约的dusdc余额
uint256 _dusdc = IERC20(dusdc).balanceOf(address(this));
if (_dusdc > 0) {
// 执行dusdc的赎回方法到当前合约,换取USDC
dERC20(dusdc).redeem(address(this), _dusdc);
}
}
/**
* @dev 收获方法
* @notice
*/
function harvest() public {
// 只能从策略账户或治理账户调用
require(
msg.sender == strategist || msg.sender == governance,
"!authorized"
);
// 获取dForce: Unipool的奖励
dRewards(pool).getReward();
// 当前合约的dForce: DF Token的余额
uint256 _df = IERC20(df).balanceOf(address(this));
// 如果dForce: DF Token余额大约0
if (_df > 0) {
// 将dForce: DF Token的余额批准给uniswap路由合约
IERC20(df).safeApprove(uni, 0);
IERC20(df).safeApprove(uni, _df);
// 交易路径dForce: DF Token => WETH => USDC
address[] memory path = new address[](3);
path[0] = df;
path[1] = weth;
path[2] = want;
// 调用uniswap用精确的token交换尽量多的token方法,用dForce: DF Token换取USDC,发送到当前合约
Uni(uni).swapExactTokensForTokens(
_df,
uint256(0),
path,
address(this),
now.add(1800)
);
}
// 当前合约的USDC余额
uint256 _want = IERC20(want).balanceOf(address(this));
// 如果USDC余额>0
if (_want > 0) {
// 手续费 = USDC余额 * 50%
uint256 _fee = _want.mul(performanceFee).div(performanceMax);
// 将手续费发送到奖励地址
IERC20(want).safeTransfer(Controller(controller).rewards(), _fee);
// 存款方法
deposit();
}
}
/**
* @dev 赎回资产方法
* @param _amount 数额
* @notice 根据当前合约在CToken的余额计算出可以在CToken中赎回的数额,并赎回资产
*/
function _withdrawSome(uint256 _amount) internal returns (uint256) {
// dusdc余额 = 提款数额 * 1e18 / dusdc的交换比例(0)
uint256 _dusdc = _amount.mul(1e18).div(dERC20(dusdc).getExchangeRate());
// 之前 = 当前合约在dusdc的余额
uint256 _before = IERC20(dusdc).balanceOf(address(this));
// 从dForce: Unipool赎回dusdc
dRewards(pool).withdraw(_dusdc);
// 之后 = 当前合约在dusdc的余额
uint256 _after = IERC20(dusdc).balanceOf(address(this));
// 提款数额 = 之后 - 之前
uint256 _withdrew = _after.sub(_before);
// 之前 = 当前合约在USDC的余额
_before = IERC20(want).balanceOf(address(this));
// 在dusdc中赎回数量为提款数额的USDC
dERC20(dusdc).redeem(address(this), _withdrew);
// 之后 = 当前合约在USDC的余额
_after = IERC20(want).balanceOf(address(this));
// 提款数额 = 之后 - 之前
_withdrew = _after.sub(_before);
// 返回提款数额
return _withdrew;
}
function balanceOfWant() public view returns (uint256) {
return IERC20(want).balanceOf(address(this));
}
function balanceOfPool() public view returns (uint256) {
return
(dRewards(pool).balanceOf(address(this)))
.mul(dERC20(dusdc).getExchangeRate())
.div(1e18);
}
function getExchangeRate() public view returns (uint256) {
return dERC20(dusdc).getExchangeRate();
}
function balanceOfDUSDC() public view returns (uint256) {
return dERC20(dusdc).getTokenBalance(address(this));
}
function balanceOf() public view returns (uint256) {
return balanceOfWant().add(balanceOfDUSDC()).add(balanceOfPool());
}
function setGovernance(address _governance) external {
require(msg.sender == governance, "!governance");
governance = _governance;
}
function setController(address _controller) external {
require(msg.sender == governance, "!governance");
controller = _controller;
}
}