-
Notifications
You must be signed in to change notification settings - Fork 216
/
Copy pathyVault.sol
171 lines (143 loc) · 5.46 KB
/
yVault.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
pragma solidity ^0.7.5;
import "openzeppelin-solidity/contracts/token/ERC20/IERC20.sol";
import "openzeppelin-solidity/contracts/math/SafeMath.sol";
import "openzeppelin-solidity/contracts/utils/Address.sol";
import "openzeppelin-solidity/contracts/token/ERC20/SafeERC20.sol";
import "openzeppelin-solidity/contracts/token/ERC20/ERC20.sol";
import "openzeppelin-solidity/contracts/access/Ownable.sol";
import "./interfaces/IController.sol";
import "./interfaces/IWETH.sol";
contract yVault is ERC20 {
using SafeERC20 for IERC20;
using Address for address;
using SafeMath for uint256;
IERC20 public token;
uint256 public min = 9500;
uint256 public constant max = 10000;
address public governance;
address public controller;
constructor(address _token, address _controller)
public
ERC20(
string(abi.encodePacked("yearn ", ERC20(_token).name())),
string(abi.encodePacked("y", ERC20(_token).symbol()))
)
{
token = IERC20(_token);
governance = msg.sender;
controller = _controller;
}
function balance() public view returns (uint256) {
return token.balanceOf(address(this)).add(IController(controller).balanceOf(address(token)));
}
function setMin(uint256 _min) external {
require(msg.sender == governance, "!governance");
min = _min;
}
function setGovernance(address _governance) public {
require(msg.sender == governance, "!governance");
governance = _governance;
}
function setController(address _controller) public {
require(msg.sender == governance, "!governance");
controller = _controller;
}
// Custom logic in here for how much the vault allows to be borrowed
// Sets minimum required on-hand to keep small withdrawals cheap
function available() public view returns (uint256) {
return token.balanceOf(address(this)).mul(min).div(max);
}
function earn() public {
uint256 _bal = available();
token.safeTransfer(controller, _bal);
IController(controller).earn(address(token), _bal);
}
function depositAll() external {
deposit(token.balanceOf(msg.sender));
}
function deposit(uint256 _amount) public {
uint256 _pool = balance();
uint256 _before = token.balanceOf(address(this));
token.safeTransferFrom(msg.sender, address(this), _amount);
uint256 _after = token.balanceOf(address(this));
_amount = _after.sub(_before); // Additional check for deflationary tokens
uint256 shares = 0;
if (totalSupply() == 0) {
shares = _amount;
} else {
shares = (_amount.mul(totalSupply())).div(_pool);
}
_mint(msg.sender, shares);
}
function withdrawAll() external {
withdraw(balanceOf(msg.sender));
}
// Used to swap any borrowed reserve over the debt limit to liquidate to 'token'
function harvest(address reserve, uint256 amount) external {
require(msg.sender == controller, "!controller");
require(reserve != address(token), "token");
IERC20(reserve).safeTransfer(controller, amount);
}
// No rebalance implementation for lower fees and faster swaps
function withdraw(uint256 _shares) public {
uint256 r = (balance().mul(_shares)).div(totalSupply());
_burn(msg.sender, _shares);
// Check balance
uint256 b = token.balanceOf(address(this));
if (b < r) {
uint256 _withdraw = r.sub(b);
IController(controller).withdraw(address(token), _withdraw);
uint256 _after = token.balanceOf(address(this));
uint256 _diff = _after.sub(b);
if (_diff < _withdraw) {
r = b.add(_diff);
}
}
token.safeTransfer(msg.sender, r);
}
function getPricePerFullShare() public view returns (uint256) {
return balance().mul(1e18).div(totalSupply());
}
// ETH deposit/withdrawals
function depositETH() public payable {
uint _pool = balance();
uint _before = token.balanceOf(address(this));
uint _amount = msg.value;
IWETH(address(token)).deposit{value: _amount}();
uint _after = token.balanceOf(address(this));
_amount = _after.sub(_before); // Additional check for deflationary tokens
uint shares = 0;
if (totalSupply() == 0) {
shares = _amount;
} else {
shares = (_amount.mul(totalSupply())).div(_pool);
}
_mint(msg.sender, shares);
}
// No rebalance implementation for lower fees and faster swaps
function withdrawETH(uint _shares) public {
uint256 r = (balance().mul(_shares)).div(totalSupply());
_burn(msg.sender, _shares);
// Check balance
uint256 b = token.balanceOf(address(this));
if (b < r) {
uint256 _withdraw = r.sub(b);
IController(controller).withdraw(address(token), _withdraw);
uint256 _after = token.balanceOf(address(this));
uint256 _diff = _after.sub(b);
if (_diff < _withdraw) {
r = b.add(_diff);
}
}
IWETH(address(token)).withdraw(r);
payable(msg.sender).call{value: r}("");
}
function withdrawAllETH() external {
withdrawETH(balanceOf(msg.sender));
}
fallback () external payable {
if (msg.sender != address(token)) {
depositETH();
}
}
}