/ contracts / finance / hedging / MetavaultHedgingManager.sol
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  }