MetavaultHedgingManager.sol
1 pragma solidity >=0.6.0; 2 pragma experimental ABIEncoderV2; 3 4 5 import "./BaseHedgingManager.sol"; 6 import "../../interfaces/ICollateralManager.sol"; 7 import "../../interfaces/IGovernableLiquidityPool.sol"; 8 import "../../interfaces/external/metavault/IPositionManager.sol"; 9 import "../../interfaces/external/metavault/IReader.sol"; 10 import "../../interfaces/external/metavault/IRouter.sol"; 11 import "../../interfaces/external/metavault/IVault.sol"; 12 import "../../interfaces/UnderlyingFeed.sol"; 13 import "../../interfaces/IMetavaultHedgingManagerFactory.sol"; 14 import "../../utils/Convert.sol"; 15 16 contract MetavaultHedgingManager is BaseHedgingManager { 17 address private positionManagerAddr; 18 address private readerAddr; 19 address private mvxRouter; 20 address private mvxVaultAddr; 21 address private metavaultHedgingManagerFactoryAddr; 22 uint private maxLeverage = 30; 23 uint private minLeverage = 1; 24 uint private defaultLeverage = 15; 25 uint constant _volumeBase = 1e18; 26 bool constant _isTestNet = true; 27 28 bytes32 private referralCode; 29 30 struct ExposureData { 31 IERC20_2 t; 32 33 int256 diff; 34 int256 real; 35 int256 ideal; 36 37 uint256 r; 38 uint256 b; 39 40 uint256 pos_size; 41 uint256 udlPrice; 42 uint256 totalStables; 43 uint256 poolLeverage; 44 uint256 totalPosValue; 45 uint256 totalHedgingStables; 46 uint256 totalPosValueToTransfer; 47 48 address underlying; 49 50 address[] at; 51 address[] _pathDecLong; 52 address[] allowedTokens; 53 uint256[] tv; 54 uint256[] openPos; 55 56 } 57 58 constructor(address _deployAddr, address _poolAddr) public { 59 poolAddr = _poolAddr; 60 Deployer deployer = Deployer(_deployAddr); 61 super.initialize(deployer); 62 metavaultHedgingManagerFactoryAddr = deployer.getContractAddress("MetavaultHedgingManagerFactory"); 63 (positionManagerAddr, readerAddr, referralCode) = IMetavaultHedgingManagerFactory(metavaultHedgingManagerFactoryAddr).getRemoteContractAddresses(); 64 require(positionManagerAddr != address(0), "bad position manager"); 65 require(readerAddr != address(0), "bad reader"); 66 mvxRouter = IPositionManager(positionManagerAddr).router(); 67 mvxVaultAddr = IPositionManager(positionManagerAddr).vault(); 68 IRouter(mvxRouter).approvePlugin(positionManagerAddr); 69 } 70 71 function pool() override external view returns (address) { 72 return poolAddr; 73 } 74 75 function getAllowedStables() public view returns (address[] memory) { 76 address[] memory allowedTokens = settings.getAllowedTokens(); 77 IVault mvxVault = IVault(mvxVaultAddr); 78 uint256 mvxAllowedTokensLen = mvxVault.allWhitelistedTokensLength(); 79 address[] memory outTokens = new address[](allowedTokens.length); 80 uint256 foundCount = 0; 81 for (uint256 i=0;i<allowedTokens.length;i++){ 82 for (uint j=0;j<mvxAllowedTokensLen;j++){ 83 address mvxAllowedToken = mvxVault.allWhitelistedTokens(j); 84 if (allowedTokens[i] == mvxAllowedToken) { 85 outTokens[i] = allowedTokens[i]; 86 foundCount++; 87 continue; 88 } 89 } 90 } 91 92 address[] memory outTokensReal = new address[](foundCount); 93 94 95 uint rIdx = 0; 96 for (uint i=0; i<allowedTokens.length; i++) { 97 if (outTokens[i] != address(0)) { 98 outTokensReal[rIdx] = outTokens[i]; 99 rIdx++; 100 } 101 } 102 103 return outTokensReal; 104 } 105 106 function getPosSize(address underlying, bool isLong) override public view returns (uint[] memory) { 107 address[] memory allowedTokens = getAllowedStables(); 108 address[] memory _collateralTokens = new address[](allowedTokens.length); 109 address[] memory _indexTokens = new address[](allowedTokens.length); 110 bool[] memory _isLong = new bool[](allowedTokens.length); 111 112 for (uint i=0; i<allowedTokens.length; i++) { 113 114 _collateralTokens[i] = allowedTokens[i]; 115 _indexTokens[i] = underlying; 116 _isLong[i] = isLong; 117 } 118 119 uint256[] memory posData = IReader(readerAddr).getPositions( 120 mvxVaultAddr, 121 address(this), 122 _collateralTokens, //need to be the approved stablecoins on dod * [long, short] 123 _indexTokens, 124 _isLong 125 ); 126 127 uint[] memory posSize = new uint[](allowedTokens.length); 128 129 for (uint i=0; i<allowedTokens.length; i++) { 130 posSize[i] = posData[i*9]; 131 } 132 133 return posSize; 134 } 135 136 function getHedgeExposure(address underlying) override public view returns (int256) { 137 address[] memory allowedTokens = getAllowedStables(); 138 address[] memory _collateralTokens = new address[](allowedTokens.length * 2); 139 address[] memory _indexTokens = new address[](allowedTokens.length * 2); 140 bool[] memory _isLong = new bool[](allowedTokens.length * 2); 141 142 for (uint i=0; i<allowedTokens.length; i++) { 143 144 _collateralTokens[i * 2] = allowedTokens[i]; 145 _collateralTokens[((i+1) * 2) - 1] = allowedTokens[i]; 146 147 _indexTokens[i * 2] = underlying; 148 _indexTokens[((i+1) * 2) - 1] = underlying; 149 150 _isLong[i * 2] = true; 151 _isLong[((i+1) * 2) - 1] = false; 152 } 153 154 155 156 uint256[] memory posData = IReader(readerAddr).getPositions( 157 mvxVaultAddr, 158 address(this), 159 _collateralTokens, //need to be the approved stablecoins on dod * [long, short] 160 _indexTokens, 161 _isLong 162 ); 163 164 //https://docs.metavault.trade/contracts#positions-list 165 166 int256 totalExposure = 0; 167 for (uint i=0; i<(allowedTokens.length*2); i++) { 168 if (posData[(i*9)] != 0) { 169 if (_isLong[i] == true) { 170 totalExposure = totalExposure.add(int256(posData[(i*9)])); 171 } else { 172 totalExposure = totalExposure.sub(int256(posData[(i*9)])); 173 } 174 } 175 } 176 177 return Convert.formatValue(totalExposure, 18, 30); 178 } 179 180 181 function idealHedgeExposure(address underlying) override public view returns (int256) { 182 // look at order book for poolAddr and compute the delta for the given underlying (depening on net positioning of the options outstanding and the side of the trade the poolAddr is on) 183 (,address[] memory _tokens, uint[] memory _holding,, uint[] memory _uncovered,, address[] memory _underlying) = exchange.getBook(poolAddr); 184 185 int totalDelta = 0; 186 for (uint i = 0; i < _tokens.length; i++) { 187 address _tk = _tokens[i]; 188 IOptionsExchange.OptionData memory opt = exchange.getOptionData(_tk); 189 if (_underlying[i] == underlying){ 190 int256 delta; 191 192 if ((_uncovered[i] > 0) && (_uncovered[i] > _holding[i])) { 193 // net short this option, thus does not need to be modified 194 delta = ICollateralManager( 195 settings.getUdlCollateralManager(opt.udlFeed) 196 ).calcDelta( 197 opt, 198 _uncovered[i].sub(_holding[i]) 199 ); 200 } 201 202 203 if (_holding[i] > 0){ 204 // net long thus needs to multiply by -1 205 delta = ICollateralManager( 206 settings.getUdlCollateralManager(opt.udlFeed) 207 ).calcDelta( 208 opt, 209 _holding[i] 210 ).mul(-1); 211 } 212 213 totalDelta = totalDelta.add(delta); 214 } 215 } 216 return totalDelta; 217 } 218 219 function realHedgeExposure(address udlFeedAddr) override public view returns (int256) { 220 // look at metavault exposure for underlying, and divide by asset price 221 (, int256 udlPrice) = UnderlyingFeed(udlFeedAddr).getLatestPrice(); 222 int256 exposure = getHedgeExposure(UnderlyingFeed(udlFeedAddr).getUnderlyingAddr()); 223 return exposure.mul(int(_volumeBase)).div(udlPrice); 224 } 225 226 function toAsciiString(address x) internal pure returns (string memory) { 227 bytes memory s = new bytes(40); 228 for (uint i = 0; i < 20; i++) { 229 bytes1 b = bytes1(uint8(uint(uint160(x)) / (2**(8*(19 - i))))); 230 bytes1 hi = bytes1(uint8(b) / 16); 231 bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi)); 232 s[2*i] = char(hi); 233 s[2*i+1] = char(lo); 234 } 235 return string(s); 236 } 237 238 function char(bytes1 b) internal pure returns (bytes1 c) { 239 if (uint8(b) < 10) return bytes1(uint8(b) + 0x30); 240 else return bytes1(uint8(b) + 0x57); 241 } 242 243 function balanceExposure(address udlFeedAddr) override external returns (bool) { 244 ExposureData memory exData; 245 exData.underlying = UnderlyingFeed(udlFeedAddr).getUnderlyingAddr(); 246 (, int256 udlPrice) = UnderlyingFeed(udlFeedAddr).getLatestPrice(); 247 exData.udlPrice = uint256(udlPrice); 248 exData.allowedTokens = getAllowedStables(); 249 exData.totalStables = creditProvider.totalTokenStock(); 250 exData.totalHedgingStables = totalTokenStock(); 251 exData.poolLeverage = (settings.isAllowedCustomPoolLeverage(poolAddr) == true) ? IGovernableLiquidityPool(poolAddr).getLeverage() : defaultLeverage; 252 require(exData.poolLeverage <= maxLeverage && exData.poolLeverage >= minLeverage, "leverage out of range"); 253 exData.ideal = idealHedgeExposure(exData.underlying); 254 exData.real = getHedgeExposure(exData.underlying).mul(int(_volumeBase)).div(udlPrice); 255 exData.diff = exData.ideal.sub(exData.real); 256 257 //dont bother to hedge if delta is below $ val threshold 258 if (uint256(MoreMath.abs(exData.diff)).mul(exData.udlPrice).div(_volumeBase) < IGovernableLiquidityPool(poolAddr).getHedgeNotionalThreshold()) { 259 return false; 260 } 261 262 263 //close out existing open pos 264 if (exData.real > 0) { 265 //need to close long position first 266 //need to loop over all available exchange stablecoins, or need to deposit underlying int to vault (if there is a vault for it) 267 exData.openPos = getPosSize(exData.underlying, true); 268 for(uint i=0; i< exData.openPos.length; i++){ 269 if (exData.openPos[i] > 0) { 270 exData.t = IERC20_2(exData.allowedTokens[i]); 271 exData._pathDecLong = new address[](2); 272 exData._pathDecLong[0] = exData.underlying; 273 exData._pathDecLong[1] = exData.allowedTokens[i]; 274 275 if (_isTestNet == true) { 276 //NOTE: THIS IS NOT ATOMIC, WILL NEED TO MANUALLY TRANSFER ANY RECIEVING STABLECOIN TO CREDIT PROVIDER AND MANUALLY CREDIT POOL BAL IN ANOTHER TX 277 IPositionManager(positionManagerAddr).decreasePositionAndSwap( 278 exData._pathDecLong, //address[] memory _path 279 exData.underlying,//address _indexToken, 280 0,//uint256 _collateralDelta, USD 1e30 mult 281 exData.openPos[i],//uint256 _sizeDelta, USD 1e30 mult 282 true,//bool _isLong, 283 address(creditProvider),//address _receiver, 284 convertPriceAndApplySlippage(exData.udlPrice, false), //uint256 _price, use current price of underlying, 5/1000 slippage? is this needed?, USD 1e30 mult 285 0//uint256(Convert.formatValue(exData.openPos[i], 18, 30)),//uint256 _minOut, TOKEN DECIMALS 286 ); 287 } else { 288 IPositionManager(positionManagerAddr).decreasePositionAndSwap( 289 exData._pathDecLong, //address[] memory _path 290 exData.underlying,//address _indexToken, 291 0,//uint256 _collateralDelta, USD 1e30 mult 292 exData.openPos[i],//uint256 _sizeDelta, USD 1e30 mult 293 true,//bool _isLong, 294 address(creditProvider),//address _receiver, 295 convertPriceAndApplySlippage(exData.udlPrice, false), //uint256 _price, use current price of underlying, 5/1000 slippage? is this needed?, USD 1e30 mult 296 0,//uint256(Convert.formatValue(exData.openPos[i], 18, 30)),//uint256 _minOut, TOKEN DECIMALS 297 referralCode//bytes32 _referralCode 298 ); 299 } 300 } 301 } 302 303 exData.pos_size = uint256(MoreMath.abs(exData.ideal)); 304 } 305 306 if (exData.real < 0) { 307 // need to close short position first 308 // need to loop over all available exchange stablecoins, or need to deposit underlying int to vault (if there is a vault for it) 309 exData.openPos = getPosSize(exData.underlying, false); 310 for(uint i=0; i< exData.openPos.length; i++){ 311 //NOTE: THIS IS NOT ATOMIC, WILL NEED TO MANUALLY TRANSFER ANY RECIEVING STABLECOIN TO CREDIT PROVIDER AND MANUALLY CREDIT POOL BAL IN ANOTHER TX 312 313 if (exData.openPos[i] > 0) { 314 315 if (_isTestNet) { 316 IPositionManager(positionManagerAddr).decreasePosition( 317 exData.allowedTokens[i],//address _collateralToken, 318 exData.underlying,//address _indexToken, 319 0,//uint256 _collateralDelta, 320 exData.openPos[i],//uint256 _sizeDelta, 321 false,//bool _isLong, 322 address(creditProvider),//address _receiver, 323 convertPriceAndApplySlippage(exData.udlPrice, true)//uint256 _price, 324 ); 325 326 } else { 327 IPositionManager(positionManagerAddr).decreasePosition( 328 exData.allowedTokens[i],//address _collateralToken, 329 exData.underlying,//address _indexToken, 330 0,//uint256 _collateralDelta, 331 exData.openPos[i],//uint256 _sizeDelta, 332 false,//bool _isLong, 333 address(creditProvider),//address _receiver, 334 convertPriceAndApplySlippage(exData.udlPrice, true),//uint256 _price, 335 referralCode//bytes32 _referralCode 336 ); 337 } 338 } 339 } 340 341 exData.pos_size = uint256(exData.ideal); 342 } 343 344 //open new pos 345 if (exData.ideal <= 0) { 346 // increase short position by pos_size 347 if (exData.pos_size != 0) { 348 exData.totalPosValue = exData.pos_size.mul(exData.udlPrice).div(_volumeBase); 349 exData.totalPosValueToTransfer = exData.totalPosValue.div(exData.poolLeverage); 350 351 require( 352 getMaxShortLiquidity(udlFeedAddr) >= exData.totalPosValue, 353 "no short hedge liq" 354 ); 355 356 // hedging should fail if not enough stables in exchange 357 if (exData.totalStables.mul(exData.poolLeverage) > exData.totalPosValue) { 358 for (uint i=0; i< exData.allowedTokens.length; i++) { 359 360 if (exData.totalPosValueToTransfer > 0) { 361 exData.t = IERC20_2(exData.allowedTokens[i]); 362 363 (exData.r, exData.b) = settings.getTokenRate(exData.allowedTokens[i]); 364 if (exData.b != 0) { 365 uint v = MoreMath.min( 366 exData.totalPosValueToTransfer, 367 exData.t.balanceOf(address(creditProvider)).mul(exData.b).div(exData.r) 368 ); 369 370 //.mul(b).div(r); //convert to exchange decimals 371 372 if (exData.t.allowance(address(this), mvxRouter) > 0) { 373 exData.t.safeApprove(mvxRouter, 0); 374 } 375 exData.t.safeApprove(mvxRouter, v.mul(exData.r).div(exData.b)); 376 377 //transfer collateral from credit provider to hedging manager and debit pool bal 378 exData.at = new address[](1); 379 exData.at[0] = exData.allowedTokens[i]; 380 381 exData.tv = new uint[](1); 382 exData.tv[0] = v; 383 384 385 if (exData.totalHedgingStables < exData.totalPosValueToTransfer){ 386 ICollateralManager( 387 settings.getUdlCollateralManager( 388 udlFeedAddr 389 ) 390 ).borrowTokensByPreference( 391 address(this), poolAddr, v, exData.at, exData.tv 392 ); 393 } 394 395 v = v.mul(exData.r).div(exData.b);//converts to token decimals 396 397 if (_isTestNet) { 398 IPositionManager(positionManagerAddr).increasePosition( 399 exData.at,//address[] memory _path, 400 exData.underlying,//address _indexToken, 401 v,//uint256 _amountIn, TOKEN DECIMALS 402 0,//uint256 _minOut, //_minOut can be zero if no swap is required , TOKEN DECIMALS 403 convertNotitionalValue(v, exData.poolLeverage, exData.b, exData.r),//uint256 _sizeDelta, USD 1e30 mult 404 false,// bool _isLong 405 convertPriceAndApplySlippage(exData.udlPrice, false)//uint256 _price USD 1e30 mult 406 ); 407 } else { 408 IPositionManager(positionManagerAddr).increasePosition( 409 exData.at,//address[] memory _path, 410 exData.underlying,//address _indexToken, 411 v,//uint256 _amountIn, TOKEN DECIMALS 412 0,//uint256 _minOut, //_minOut can be zero if no swap is required , TOKEN DECIMALS 413 convertNotitionalValue(v, exData.poolLeverage, exData.b, exData.r),//uint256 _sizeDelta, USD 1e30 mult 414 false,// bool _isLong 415 convertPriceAndApplySlippage(exData.udlPrice, false),//uint256 _price, USD 1e30 mult 416 referralCode//bytes32 _referralCode 417 ); 418 } 419 420 //back to exchange decimals 421 422 if (exData.totalPosValueToTransfer > v.mul(exData.r).div(exData.b)) { 423 exData.totalPosValueToTransfer = exData.totalPosValueToTransfer.sub(v.mul(exData.r).div(exData.b)); 424 425 } else { 426 exData.totalPosValueToTransfer = 0; 427 } 428 429 exData.r = 0; 430 exData.b = 0; 431 } 432 } 433 } 434 } 435 436 return true; 437 } 438 } else if (exData.ideal > 0) { 439 440 // increase long position by pos_size 441 if (exData.pos_size != 0) { 442 exData.totalPosValue = exData.pos_size.mul(exData.udlPrice).div(_volumeBase); 443 exData.totalPosValueToTransfer = exData.totalPosValue.div(exData.poolLeverage); 444 445 require( 446 getMaxLongLiquidity(udlFeedAddr) >= exData.totalPosValue, 447 "no long hedge liq" 448 ); 449 450 // hedging should fail if not enough stables in exchange 451 if (exData.totalStables.mul(exData.poolLeverage) > exData.totalPosValue) { 452 for (uint i=0; i< exData.allowedTokens.length; i++) { 453 454 if (exData.totalPosValueToTransfer > 0) { 455 exData.t = IERC20_2(exData.allowedTokens[i]); 456 457 (exData.r, exData.b) = settings.getTokenRate(exData.allowedTokens[i]); 458 if (exData.b != 0) { 459 uint v = MoreMath.min( 460 exData.totalPosValueToTransfer, 461 exData.t.balanceOf(address(creditProvider)).mul(exData.b).div(exData.r) 462 ); 463 if (exData.t.allowance(address(this), mvxRouter) > 0) { 464 exData.t.safeApprove(mvxRouter, 0); 465 } 466 exData.t.safeApprove(mvxRouter, v.mul(exData.r).div(exData.b)); 467 468 //transfer collateral from credit provider to hedging manager and debit pool bal 469 exData.at = new address[](1); 470 address[] memory at_s = new address[](2); 471 exData.at[0] = exData.allowedTokens[i]; 472 473 at_s[0] = exData.allowedTokens[i]; 474 at_s[1] = exData.underlying; 475 476 exData.tv = new uint[](1); 477 exData.tv[0] = v; 478 479 if (exData.totalHedgingStables < exData.totalPosValueToTransfer){ 480 ICollateralManager( 481 settings.getUdlCollateralManager( 482 udlFeedAddr 483 ) 484 ).borrowTokensByPreference( 485 address(this), poolAddr, v, exData.at, exData.tv 486 ); 487 } 488 489 v = v.mul(exData.r).div(exData.b);//converts to token decimals 490 491 492 if (_isTestNet) { 493 IPositionManager(positionManagerAddr).increasePosition( 494 at_s,//address[] memory _path, 495 exData.underlying,//address _indexToken, 496 v,//uint256 _amountIn, TOKEN DECIMALS 497 0,//Convert.formatValue(v.div(exData.udlPrice).mul(exData.b).div(exData.r), 30, 18),//uint256 _minOut, //_minOut can be zero if no swap is required , TOKEN DECIMALS 498 convertNotitionalValue(v, exData.poolLeverage, exData.b, exData.r),//uint256 _sizeDelta, USD 1e30 mult 499 true,// bool _isLong 500 convertPriceAndApplySlippage(exData.udlPrice, true)//uint256 _price, USD 1e30 mult 501 ); 502 } else { 503 IPositionManager(positionManagerAddr).increasePosition( 504 at_s,//address[] memory _path, 505 exData.underlying,//address _indexToken, 506 v,//uint256 _amountIn, TOKEN DECIMALS 507 0,//Convert.formatValue(v.div(exData.udlPrice).mul(exData.b).div(exData.r), 30, 18),//uint256 _minOut, //_minOut can be zero if no swap is required , TOKEN DECIMALS 508 convertNotitionalValue(v, exData.poolLeverage, exData.b, exData.r),//uint256 _sizeDelta, USD 1e30 mult 509 true,// bool _isLong 510 convertPriceAndApplySlippage(exData.udlPrice, true),//uint256 _price, USD 1e30 mult 511 referralCode//bytes32 _referralCode 512 ); 513 } 514 515 //back to exchange decimals 516 if (exData.totalPosValueToTransfer > v.mul(exData.r).div(exData.b)) { 517 exData.totalPosValueToTransfer = exData.totalPosValueToTransfer.sub(v.mul(exData.r).div(exData.b)); 518 519 } else { 520 exData.totalPosValueToTransfer = 0; 521 } 522 exData.r = 0; 523 exData.b = 0; 524 } 525 } 526 } 527 } 528 529 return true; 530 } 531 } 532 533 return false; 534 } 535 536 function getMaxLongLiquidity(address udlFeedAddr) public view returns (uint v) { 537 /* 538 539 - To calculate the available amount of liquidity for long positions: 540 541 indexToken: the address of the token to long 542 Available amount in tokens: Vault.poolAmounts(indexToken) - Vault.reservedAmounts(indexToken) 543 Available amount in USD: PositionRouter.maxGlobalLongSizes(indexToken) - Vault.guaranteedUsd(indexToken) 544 The available liquidity will be the lower of these two values 545 PositionRouter.maxGlobalLongSizes(indexToken) can be zero, in which case there is no additional cap, and available liquidity is based only on the available amount of tokens 546 */ 547 548 ExposureData memory exData; 549 exData.underlying = UnderlyingFeed(udlFeedAddr).getUnderlyingAddr(); 550 IVault mvxVault = IVault(mvxVaultAddr); 551 552 uint256 vliq = mvxVault.poolAmounts(exData.underlying).sub(mvxVault.reservedAmounts(exData.underlying)); 553 uint256 pegliq = IPositionManager(positionManagerAddr).maxGlobalLongSizes(exData.underlying).sub(mvxVault.guaranteedUsd(exData.underlying)); 554 555 uint256 totalLiq = MoreMath.min(vliq, pegliq); 556 557 return Convert.formatValue(totalLiq, 18, 30); 558 559 } 560 561 function getMaxShortLiquidity(address udlFeedAddr) public view returns (uint v) { 562 /* 563 -To calculate the available amount of liquidity for short positions: 564 indexToken: the address of the token to short 565 collateralToken: the address of the stablecoin token to be used as collateral 566 Available amount in tokens: Vault.poolAmounts(collateralToken) - Vault.reservedAmounts(collateralToken) 567 Available amount in USD: PositionRouter.maxGlobalShortSizes(indexToken) - Vault.globalShortSizes(indexToken) 568 The available liquidity will be the lower of these two values 569 PositionRouter.maxGlobalShortSizes(indexToken) can be zero, in which case there is no additional cap, and available liquidity is based only on the available amount of tokens 570 */ 571 address[] memory tokens = getAllowedStables(); 572 ExposureData memory exData; 573 exData.underlying = UnderlyingFeed(udlFeedAddr).getUnderlyingAddr(); 574 IVault mvxVault = IVault(mvxVaultAddr); 575 IPositionManager mvxPositionManager = IPositionManager(positionManagerAddr); 576 uint256 totalLiq = 0; 577 578 for(uint i=0; i<tokens.length; i++) { 579 uint256 vliq = mvxVault.poolAmounts(tokens[i]).sub(mvxVault.reservedAmounts(tokens[i])); 580 uint256 pegliq = mvxPositionManager.maxGlobalShortSizes(exData.underlying).sub(mvxVault.globalShortSizes(exData.underlying)); 581 totalLiq = totalLiq.add(MoreMath.min(vliq, pegliq)); 582 } 583 584 return Convert.formatValue(totalLiq, 18, 30); 585 586 } 587 588 function totalTokenStock() override public view returns (uint v) { 589 590 address[] memory tokens = getAllowedStables(); 591 for (uint i = 0; i < tokens.length; i++) { 592 (uint r, uint b) = settings.getTokenRate(tokens[i]); 593 uint value = IERC20_2(tokens[i]).balanceOf(address(this)); 594 v = v.add(value.mul(b).div(r)); 595 } 596 } 597 598 function convertNotitionalValue(uint256 value, uint256 multiplier, uint256 b, uint256 r) pure internal returns (uint256) { 599 return Convert.formatValue(value.mul(multiplier).mul(b).div(r), 30, 18); 600 } 601 602 function convertPriceAndApplySlippage(uint256 value, bool isAdd) pure internal returns (uint256) { 603 if (isAdd) { 604 return uint256(Convert.formatValue(value.add(value.mul(3).div(1000)), 30, 18)); 605 } else { 606 return uint256(Convert.formatValue(value.sub(value.mul(3).div(1000)), 30, 18)); 607 } 608 609 } 610 611 function transferTokensToCreditProvider(address tokenAddr) override external { 612 //this needs to be used if/when liquidations happen and tokens sent from external contracts end up here 613 uint value = IERC20_2(tokenAddr).balanceOf(address(this)); 614 if (value > 0) { 615 IERC20_2(tokenAddr).safeTransfer(address(creditProvider), value); 616 creditProvider.creditPoolBalance(poolAddr, tokenAddr, value); 617 } 618 } 619 }