-
Notifications
You must be signed in to change notification settings - Fork 574
Expand file tree
/
Copy pathGlpManager.sol
More file actions
211 lines (165 loc) · 8.04 KB
/
GlpManager.sol
File metadata and controls
211 lines (165 loc) · 8.04 KB
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
// SPDX-License-Identifier: MIT
import "../libraries/math/SafeMath.sol";
import "../libraries/token/IERC20.sol";
import "../libraries/token/SafeERC20.sol";
import "../libraries/utils/ReentrancyGuard.sol";
import "./interfaces/IVault.sol";
import "./interfaces/IGlpManager.sol";
import "../tokens/interfaces/IUSDG.sol";
import "../tokens/interfaces/IMintable.sol";
import "../access/Governable.sol";
pragma solidity 0.6.12;
contract GlpManager is ReentrancyGuard, Governable, IGlpManager {
using SafeMath for uint256;
using SafeERC20 for IERC20;
uint256 public constant PRICE_PRECISION = 10 ** 30;
uint256 public constant USDG_DECIMALS = 18;
uint256 public constant MAX_COOLDOWN_DURATION = 48 hours;
IVault public vault;
address public usdg;
address public glp;
uint256 public override cooldownDuration;
mapping (address => uint256) public override lastAddedAt;
uint256 public aumAddition;
uint256 public aumDeduction;
bool public inPrivateMode;
mapping (address => bool) public isHandler;
event AddLiquidity(
address account,
address token,
uint256 amount,
uint256 aumInUsdg,
uint256 glpSupply,
uint256 usdgAmount,
uint256 mintAmount
);
event RemoveLiquidity(
address account,
address token,
uint256 glpAmount,
uint256 aumInUsdg,
uint256 glpSupply,
uint256 usdgAmount,
uint256 amountOut
);
constructor(address _vault, address _usdg, address _glp, uint256 _cooldownDuration) public {
gov = msg.sender;
vault = IVault(_vault);
usdg = _usdg;
glp = _glp;
cooldownDuration = _cooldownDuration;
}
function setInPrivateMode(bool _inPrivateMode) external onlyGov {
inPrivateMode = _inPrivateMode;
}
function setHandler(address _handler, bool _isActive) external onlyGov {
isHandler[_handler] = _isActive;
}
function setCooldownDuration(uint256 _cooldownDuration) external onlyGov {
require(_cooldownDuration <= MAX_COOLDOWN_DURATION, "GlpManager: invalid _cooldownDuration");
cooldownDuration = _cooldownDuration;
}
function setAumAdjustment(uint256 _aumAddition, uint256 _aumDeduction) external onlyGov {
aumAddition = _aumAddition;
aumDeduction = _aumDeduction;
}
function addLiquidity(address _token, uint256 _amount, uint256 _minUsdg, uint256 _minGlp) external override nonReentrant returns (uint256) {
if (inPrivateMode) { revert("GlpManager: action not enabled"); }
return _addLiquidity(msg.sender, msg.sender, _token, _amount, _minUsdg, _minGlp);
}
function addLiquidityForAccount(address _fundingAccount, address _account, address _token, uint256 _amount, uint256 _minUsdg, uint256 _minGlp) external override nonReentrant returns (uint256) {
_validateHandler();
return _addLiquidity(_fundingAccount, _account, _token, _amount, _minUsdg, _minGlp);
}
function removeLiquidity(address _tokenOut, uint256 _glpAmount, uint256 _minOut, address _receiver) external override nonReentrant returns (uint256) {
if (inPrivateMode) { revert("GlpManager: action not enabled"); }
return _removeLiquidity(msg.sender, _tokenOut, _glpAmount, _minOut, _receiver);
}
function removeLiquidityForAccount(address _account, address _tokenOut, uint256 _glpAmount, uint256 _minOut, address _receiver) external override nonReentrant returns (uint256) {
_validateHandler();
return _removeLiquidity(_account, _tokenOut, _glpAmount, _minOut, _receiver);
}
function getAums() public view returns (uint256[] memory) {
uint256[] memory amounts = new uint256[](2);
amounts[0] = getAum(true);
amounts[1] = getAum(false);
return amounts;
}
function getAumInUsdg(bool maximise) public view returns (uint256) {
uint256 aum = getAum(maximise);
return aum.mul(10 ** USDG_DECIMALS).div(PRICE_PRECISION);
}
function getAum(bool maximise) public view returns (uint256) {
uint256 length = vault.allWhitelistedTokensLength();
uint256 aum = aumAddition;
uint256 shortProfits = 0;
for (uint256 i = 0; i < length; i++) {
address token = vault.allWhitelistedTokens(i);
bool isWhitelisted = vault.whitelistedTokens(token);
if (!isWhitelisted) {
continue;
}
uint256 price = maximise ? vault.getMaxPrice(token) : vault.getMinPrice(token);
uint256 poolAmount = vault.poolAmounts(token);
uint256 decimals = vault.tokenDecimals(token);
if (vault.stableTokens(token)) {
aum = aum.add(poolAmount.mul(price).div(10 ** decimals));
} else {
// add global short profit / loss
uint256 size = vault.globalShortSizes(token);
if (size > 0) {
uint256 averagePrice = vault.globalShortAveragePrices(token);
uint256 priceDelta = averagePrice > price ? averagePrice.sub(price) : price.sub(averagePrice);
uint256 delta = size.mul(priceDelta).div(averagePrice);
if (price > averagePrice) {
// add losses from shorts
aum = aum.add(delta);
} else {
shortProfits = shortProfits.add(delta);
}
}
aum = aum.add(vault.guaranteedUsd(token));
uint256 reservedAmount = vault.reservedAmounts(token);
aum = aum.add(poolAmount.sub(reservedAmount).mul(price).div(10 ** decimals));
}
}
aum = shortProfits > aum ? 0 : aum.sub(shortProfits);
return aumDeduction > aum ? 0 : aum.sub(aumDeduction);
}
function _addLiquidity(address _fundingAccount, address _account, address _token, uint256 _amount, uint256 _minUsdg, uint256 _minGlp) private returns (uint256) {
require(_amount > 0, "GlpManager: invalid _amount");
// calculate aum before buyUSDG
uint256 aumInUsdg = getAumInUsdg(true);
uint256 glpSupply = IERC20(glp).totalSupply();
IERC20(_token).safeTransferFrom(_fundingAccount, address(vault), _amount);
uint256 usdgAmount = vault.buyUSDG(_token, address(this));
require(usdgAmount >= _minUsdg, "GlpManager: insufficient USDG output");
uint256 mintAmount = aumInUsdg == 0 ? usdgAmount : usdgAmount.mul(glpSupply).div(aumInUsdg);
require(mintAmount >= _minGlp, "GlpManager: insufficient GLP output");
IMintable(glp).mint(_account, mintAmount);
lastAddedAt[_account] = block.timestamp;
emit AddLiquidity(_account, _token, _amount, aumInUsdg, glpSupply, usdgAmount, mintAmount);
return mintAmount;
}
function _removeLiquidity(address _account, address _tokenOut, uint256 _glpAmount, uint256 _minOut, address _receiver) private returns (uint256) {
require(_glpAmount > 0, "GlpManager: invalid _glpAmount");
require(lastAddedAt[_account].add(cooldownDuration) <= block.timestamp, "GlpManager: cooldown duration not yet passed");
// calculate aum before sellUSDG
uint256 aumInUsdg = getAumInUsdg(false);
uint256 glpSupply = IERC20(glp).totalSupply();
uint256 usdgAmount = _glpAmount.mul(aumInUsdg).div(glpSupply);
uint256 usdgBalance = IERC20(usdg).balanceOf(address(this));
if (usdgAmount > usdgBalance) {
IUSDG(usdg).mint(address(this), usdgAmount.sub(usdgBalance));
}
IMintable(glp).burn(_account, _glpAmount);
IERC20(usdg).transfer(address(vault), usdgAmount);
uint256 amountOut = vault.sellUSDG(_tokenOut, _receiver);
require(amountOut >= _minOut, "GlpManager: insufficient output");
emit RemoveLiquidity(_account, _tokenOut, _glpAmount, aumInUsdg, glpSupply, usdgAmount, amountOut);
return amountOut;
}
function _validateHandler() private view {
require(isHandler[msg.sender], "GlpManager: forbidden");
}
}