UnderlyingVault.sol
1 pragma solidity >=0.6.0; 2 3 import "../deployment/ManagedContract.sol"; 4 import "../interfaces/IProtocolSettings.sol"; 5 import "../interfaces/IERC20.sol"; 6 import "../interfaces/IERC20Details.sol"; 7 import "../interfaces/IUniswapV2Router01.sol"; 8 import "../interfaces/TimeProvider.sol"; 9 import "../interfaces/UnderlyingFeed.sol"; 10 import "../interfaces/ICreditProvider.sol"; 11 import "../interfaces/IUnderlyingCreditProvider.sol"; 12 import "../utils/Convert.sol"; 13 import "../utils/MoreMath.sol"; 14 import "../utils/SafeERC20.sol"; 15 16 contract UnderlyingVault is ManagedContract { 17 18 using SafeERC20 for IERC20_2; 19 using SafeMath for uint; 20 using SignedSafeMath for int; 21 22 uint private fractionBase = 1e9; 23 24 TimeProvider private time; 25 IProtocolSettings private settings; 26 ICreditProvider private creditProvider; 27 28 mapping(address => uint) private callers; 29 mapping(address => mapping(address => uint)) private _totalSupplyRehypothicated;//token-> protocol->amount 30 mapping(address => mapping(address => uint)) private _totalSupplyShareRehypothicated;//token share-> protocol->amount 31 mapping(address => bool) private _isRehypothicate; 32 mapping(address => mapping(address => uint)) private allocation; 33 mapping(address => mapping(address => mapping(address => uint))) private _rehypothecationAllocation;//token->protocol->user->amount 34 mapping(address => mapping(address => address)) private _activeUserRehypothicationProtocol;//token->user->active protocol 35 36 mapping(address => uint256) _totalSupply; 37 mapping(address => address) _underlyingCreditProvider; 38 39 struct tsVars { 40 uint balR; 41 uint valR; 42 uint rBalR; 43 } 44 45 46 event Lock(address indexed owner, address indexed token, uint value); 47 48 event Liquidate(address indexed owner, address indexed token, uint valueIn, uint valueOut); 49 50 event Release(address indexed owner, address indexed token, uint value); 51 52 function initialize(Deployer deployer) override internal { 53 54 time = TimeProvider(deployer.getContractAddress("TimeProvider")); 55 settings = IProtocolSettings(deployer.getContractAddress("ProtocolSettings")); 56 creditProvider = ICreditProvider(deployer.getContractAddress("CreditProvider")); 57 58 callers[deployer.getContractAddress("OptionsExchange")] = 1; 59 callers[deployer.getContractAddress("CollateralManager")] = 1; 60 callers[deployer.getContractAddress("Incentivized")] = 1; 61 callers[address(settings)] = 1; 62 } 63 64 65 function setup(Deployer deployer) public { 66 67 time = TimeProvider(deployer.getContractAddress("TimeProvider")); 68 settings = IProtocolSettings(deployer.getContractAddress("ProtocolSettings")); 69 creditProvider = ICreditProvider(deployer.getContractAddress("CreditProvider")); 70 71 callers[deployer.getContractAddress("OptionsExchange")] = 1; 72 callers[deployer.getContractAddress("CollateralManager")] = 1; 73 callers[deployer.getContractAddress("Incentivized")] = 1; 74 callers[address(settings)] = 1; 75 } 76 77 function balanceOf(address owner, address token) public view returns (uint) { 78 79 return allocation[owner][token]; 80 } 81 82 function totalSupply(address token) public view returns (uint) { 83 return _totalSupply[token]; 84 } 85 86 function getUnderlyingCreditProvider(address token) public view returns (address) { 87 return _underlyingCreditProvider[token]; 88 } 89 90 function setUnderlyingCreditProvider(address token, address udlCreditProviderAddress) external { 91 ensureCaller(); 92 require(udlCreditProviderAddress != address(0), "bad udlcdprov"); 93 require(_underlyingCreditProvider[token] == address(0), "existing udlcdprov"); 94 _underlyingCreditProvider[token] = udlCreditProviderAddress; 95 } 96 97 function balanceOfRehypothicatedShares(address owner, address token, address rehypothicationManager) public view returns (uint) { 98 return _rehypothecationAllocation[token][rehypothicationManager][owner]; 99 } 100 101 function addUnderlyingShareBalanceRehypothicated(address owner, address token, address rehypothicationManager, uint v) private { 102 _rehypothecationAllocation[token][rehypothicationManager][owner] = _rehypothecationAllocation[token][rehypothicationManager][owner].add(v); 103 _totalSupplyShareRehypothicated[token][rehypothicationManager] = _totalSupplyShareRehypothicated[token][rehypothicationManager].add(v); 104 } 105 106 function removeUnderlyingSharesBalanceRehypothicated(address owner, address token, address rehypothicationManager, uint v) private { 107 _rehypothecationAllocation[token][rehypothicationManager][owner] = _rehypothecationAllocation[token][rehypothicationManager][owner].sub(v); 108 _totalSupplyShareRehypothicated[token][rehypothicationManager] = _totalSupplyShareRehypothicated[token][rehypothicationManager].sub(v); 109 } 110 111 function totalSupplyRehypothicated(address token, address rehypothicationManager) public view returns (uint) { 112 return _totalSupplyRehypothicated[token][rehypothicationManager]; 113 } 114 115 116 function totalSupplySharesRehypothicated(address token, address rehypothicationManager) public view returns (uint) { 117 return _totalSupplyShareRehypothicated[token][rehypothicationManager] ; 118 } 119 120 function addUnderlyingSupplyRehypothicated(address token, address rehypothicationManager, uint value) private { 121 _totalSupplyRehypothicated[token][rehypothicationManager] = _totalSupplyRehypothicated[token][rehypothicationManager].add(value); 122 } 123 124 function removeUnderlyingSupplyRehypothicated(address token, address rehypothicationManager, uint value) private { 125 _totalSupplyRehypothicated[token][rehypothicationManager] = _totalSupplyRehypothicated[token][rehypothicationManager].sub(value); 126 } 127 128 function lock(address owner, address token, uint value, bool isRehypothicate, address rehypothicationManager) external { 129 130 ensureCaller(); 131 132 require(owner != address(0), "invalid owner"); 133 require(token != address(0), "invalid token"); 134 135 allocation[owner][token] = allocation[owner][token].add(value); 136 _totalSupply[token] = _totalSupply[token].add(value); 137 138 if (isRehypothicate == true) { 139 _isRehypothicate[owner] = true; 140 141 require(settings.isAllowedRehypothicationManager(rehypothicationManager) == true, "rehyM not allowed"); 142 143 if (_activeUserRehypothicationProtocol[token][owner] != rehypothicationManager){ 144 //needs to be null if diff 145 require(_activeUserRehypothicationProtocol[token][owner] == address(0), "rehyM already in use"); 146 } else { 147 //is null addr, set 148 _activeUserRehypothicationProtocol[token][owner] = rehypothicationManager; 149 } 150 151 uint b0 = totalSupplyRehypothicated(token, rehypothicationManager); 152 addUnderlyingSupplyRehypothicated(token, rehypothicationManager, value); 153 //transfer tokens for rehypothication to credit provider 154 IUnderlyingCreditProvider(_underlyingCreditProvider[token]).depositTokens( 155 owner, 156 token, 157 Convert.from18DecimalsBase(token, value) 158 ); 159 uint b1 = totalSupplyRehypothicated(token, rehypothicationManager); 160 uint p = b1.sub(b0).mul(fractionBase).div(b1); 161 uint b = 1e3; 162 uint v = totalSupplySharesRehypothicated(token, rehypothicationManager) > 0 ? 163 totalSupplySharesRehypothicated(token, rehypothicationManager).mul(p).mul(b).div(fractionBase.sub(p)) : 164 b1.mul(b); 165 v = MoreMath.round(v, b); 166 167 addUnderlyingShareBalanceRehypothicated(owner, token, rehypothicationManager, v); 168 169 } 170 171 emit Lock(owner, token, value); 172 } 173 174 function liquidate( 175 address owner, 176 address token, 177 address feed, 178 uint amountOut 179 ) 180 external 181 returns (uint _in, uint _out) 182 { 183 ensureCaller(); 184 185 require(owner != address(0), "invalid owner"); 186 require(token != address(0), "invalid token"); 187 require(feed != address(0), "invalid feed"); 188 189 190 uint balance = balanceOf(owner, token); 191 192 if (balance > 0) { 193 194 (address _router, address _stablecoin) = settings.getSwapRouterInfo(); 195 require( 196 _router != address(0) && _stablecoin != address(0), 197 "invalid swap router settings" 198 ); 199 200 (, int p) = UnderlyingFeed(feed).getLatestPrice(); 201 202 //NEED TO DO THIS BEFORE CALLING `swapUnderlyingForStablecoin` in order to make sure enough token in vault (by transfering all of the users balance to vault, then depositing back to user what was liquidated) 203 uint udlValB = IUnderlyingCreditProvider(_underlyingCreditProvider[token]).balanceOf(owner); 204 IUnderlyingCreditProvider(_underlyingCreditProvider[token]).grantTokens(address(this), udlValB); 205 206 (_in, _out) = swapUnderlyingForStablecoin( 207 owner, 208 IUniswapV2Router01(_router), 209 settings.getSwapPath( 210 token, 211 _stablecoin 212 ), 213 p, 214 balance, 215 amountOut 216 ); 217 218 uint udlValO = udlValB.sub(_in); 219 allocation[owner][token] = allocation[owner][token].sub(_in); 220 _totalSupply[token] = _totalSupply[token].sub(_in); 221 222 //send residual back to cdtp 223 if (udlValO > 0) { 224 IERC20_2(token).safeTransfer(_underlyingCreditProvider[token], Convert.from18DecimalsBase(token, udlValO)); 225 } 226 227 if (_isRehypothicate[owner] == true){ 228 address rehypothicationManager = _activeUserRehypothicationProtocol[token][owner]; 229 230 if (rehypothicationManager != address(0)) { 231 tsVars memory tsv; 232 tsv.balR = balanceOfRehypothicatedShares(owner, token, rehypothicationManager); 233 tsv.valR = valueOfRehypothicatedShares(owner, token, rehypothicationManager); 234 tsv.rBalR = _in.mul(tsv.balR).div(tsv.valR); 235 236 removeUnderlyingSharesBalanceRehypothicated(owner, token, rehypothicationManager, tsv.rBalR); 237 removeUnderlyingSupplyRehypothicated(token, rehypothicationManager, _in); 238 checkAndResetRehypothicationManager(owner, token, rehypothicationManager); 239 } 240 } 241 242 emit Liquidate(owner, token, _in, _out); 243 } 244 } 245 246 function valueOfRehypothicatedShares(address ownr, address token, address rehypothicationManager) public view returns (uint) { 247 uint bal = _totalSupplyShareRehypothicated[token][rehypothicationManager]; 248 uint balOwnr = balanceOfRehypothicatedShares(ownr, token, rehypothicationManager); 249 return uint(int(bal)) 250 .mul(balOwnr).div(_totalSupplyRehypothicated[token][rehypothicationManager]); 251 } 252 253 function release(address owner, address token, address feed, uint value) external { 254 255 ensureCaller(); 256 257 require(owner != address(0), "invalid owner"); 258 require(token != address(0), "invalid token"); 259 require(feed != address(0), "invalid feed"); 260 261 uint bal = allocation[owner][token]; 262 value = MoreMath.min(bal, value); 263 264 if (bal > 0) { 265 266 allocation[owner][token] = bal.sub(value); 267 _totalSupply[token] = _totalSupply[token].sub(value); 268 269 if (_isRehypothicate[owner] == true){ 270 address rehypothicationManager = _activeUserRehypothicationProtocol[token][owner]; 271 272 if (rehypothicationManager != address(0)) { 273 uint balR = balanceOfRehypothicatedShares(owner, token, rehypothicationManager); 274 value = valueOfRehypothicatedShares(owner, token, rehypothicationManager); 275 removeUnderlyingSharesBalanceRehypothicated(owner, token, rehypothicationManager, balR); 276 removeUnderlyingSupplyRehypothicated(token, rehypothicationManager, value); 277 checkAndResetRehypothicationManager(owner, token, rehypothicationManager); 278 } 279 280 IUnderlyingCreditProvider(_underlyingCreditProvider[token]).withdrawTokens(owner, value); 281 } else { 282 address underlying = UnderlyingFeed(feed).getUnderlyingAddr(); 283 uint v = Convert.from18DecimalsBase(underlying, value); 284 IERC20_2(underlying).safeTransfer(owner, v); 285 } 286 287 emit Release(owner, token, value); 288 } 289 } 290 291 function checkAndResetRehypothicationManager(address owner, address token, address rehypothicationManager) private { 292 uint balR = balanceOfRehypothicatedShares(owner, token, rehypothicationManager); 293 if (balR == 0) { 294 //unset rehyM 295 _activeUserRehypothicationProtocol[token][owner] = address(0); 296 } 297 } 298 299 function swapUnderlyingForStablecoin( 300 address owner, 301 IUniswapV2Router01 router, 302 address[] memory path, 303 int price, 304 uint balance, 305 uint amountOut 306 ) 307 private 308 returns (uint _in, uint _out) 309 { 310 require(path.length >= 2, "invalid swap path"); 311 312 (uint r, uint b) = settings.getTokenRate(path[path.length - 1]); 313 314 uint udlBalance = Convert.from18DecimalsBase(path[0], balance); 315 316 uint amountInMax = getAmountInMax( 317 price, 318 amountOut, 319 path 320 ); 321 322 if (amountInMax > udlBalance) { 323 amountOut = amountOut.mul(udlBalance).div(amountInMax); 324 amountInMax = udlBalance; 325 } 326 327 IERC20_2 tk = IERC20_2(path[0]); 328 if (tk.allowance(address(this), address(router)) > 0) { 329 tk.safeApprove(address(router), 0); 330 } 331 tk.safeApprove(address(router), amountInMax); 332 333 _out = amountOut; 334 _in = router.swapTokensForExactTokens( 335 amountOut.mul(r).div(b), 336 amountInMax, 337 path, 338 address(creditProvider), 339 time.getNow() 340 )[0]; 341 _in = Convert.to18DecimalsBase(path[0], _in); 342 343 if (amountOut > 0) { 344 creditProvider.addBalance(owner, path[path.length - 1], amountOut.mul(r).div(b)); 345 } 346 } 347 348 function getAmountInMax( 349 int price, 350 uint amountOut, 351 address[] memory path 352 ) 353 private 354 view 355 returns (uint amountInMax) 356 { 357 uint8 d = IERC20Details(path[0]).decimals(); 358 amountInMax = amountOut.mul(10 ** uint(d)).div(uint(price)); 359 360 (uint rTol, uint bTol) = settings.getSwapRouterTolerance(); 361 amountInMax = amountInMax.mul(rTol).div(bTol); 362 } 363 364 function getAmountInMaxInv( 365 int price, 366 uint amountOut, 367 address[] memory path 368 ) 369 public 370 view 371 returns (uint amountInMax) 372 { 373 uint8 d = IERC20Details(path[0]).decimals(); 374 amountInMax = amountOut.mul(10 ** uint(d)).mul(uint(price)); 375 376 (uint rTol, uint bTol) = settings.getSwapRouterTolerance(); 377 amountInMax = amountInMax.mul(rTol).div(bTol); 378 } 379 380 function ensureCaller() private view { 381 382 require(callers[msg.sender] == 1, "Vault: unauthorized caller"); 383 } 384 }