/ contracts / finance / hedging / D8xHedgingManager.sol
D8xHedgingManager.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/d8x/ID8xPerpetualsContractInterface.sol";
  9  import "../../interfaces/AggregatorV3Interface.sol";
 10  import "../../interfaces/UnderlyingFeed.sol";
 11  import "../../interfaces/ID8xHedgingManagerFactory.sol";
 12  import "../../utils/Convert.sol";
 13  
 14  
 15  contract D8xHedgingManager is BaseHedgingManager {
 16      address private orderBookAddr;
 17      address private perpetualProxy;
 18      address private d8xHedgingManagerFactoryAddr;
 19      uint private maxLeverage = 30;
 20      uint private minLeverage = 1;
 21      uint private defaultLeverage = 15;
 22      uint constant _volumeBase = 1e18;
 23  
 24      event PerpOrderSubmitFailed(string reason);
 25      event PerpOrderSubmitSuccess(int256 amountDec18, int16 leverageInteger);
 26          
 27      int256 private constant DECIMALS = 10**18;
 28      int128 private constant ONE_64x64 = 0x010000000000000000;
 29      int128 private constant MIN_64x64 = -0x80000000000000000000000000000000;
 30      int128 private constant MAX_64x64 = 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF;
 31      int128 private constant TEN_64x64 = 0xa0000000000000000;
 32  
 33      struct ExposureData {
 34          IERC20_2 t;
 35  
 36          int256 diff;
 37          int256 real;
 38          int256 ideal;
 39  
 40          uint256 r;
 41          uint256 b;
 42          
 43          uint256 pos_size;
 44          uint256 udlPrice;
 45          uint256 totalStables;
 46          uint256 poolLeverage;
 47          uint256 totalPosValue;
 48          uint256 totalHedgingStables;
 49          uint256 totalPosValueToTransfer;
 50          
 51          address underlying;
 52          string underlyingStr;
 53          
 54          address[] at;
 55          address[] _pathDecLong;
 56          address[] allowedTokens;
 57          uint256[] tv;
 58          int256[] openPos;
 59          uint24[] perpIds;
 60          
 61      }
 62  
 63      constructor(address _deployAddr, address _poolAddr) public {
 64          poolAddr = _poolAddr;
 65          Deployer deployer = Deployer(_deployAddr);
 66          super.initialize(deployer);
 67          d8xHedgingManagerFactoryAddr = deployer.getContractAddress("D8xHedgingManagerFactory");
 68          (address _d8xOrderBookAddr,address _perpetualProxy) = ID8xHedgingManagerFactory(d8xHedgingManagerFactoryAddr).getRemoteContractAddresses();
 69          
 70          require(_d8xOrderBookAddr != address(0), "bad order book");
 71          require(_perpetualProxy != address(0), "bad perp proxy");
 72          
 73          orderBookAddr = _d8xOrderBookAddr;
 74          perpetualProxy = _perpetualProxy;
 75      }
 76  
 77      function pool() override external view returns (address) {
 78          return poolAddr;
 79      }
 80  
 81      /**
 82       * Post an order to the order book. Order will be executed by
 83       * external "keepers".
 84       * @param _amountDec18 signed amount to be traded
 85       * @param _leverageInteger leverage (integer), e.g. 2 for 2x leverage
 86       * @return true if posting order succeeded
 87       */
 88  
 89      /**
 90       * @notice
 91       * Available order flags:
 92       *  uint32 internal constant MASK_CLOSE_ONLY = 0x80000000;
 93       *  uint32 internal constant MASK_MARKET_ORDER = 0x40000000;
 94       *  uint32 internal constant MASK_STOP_ORDER = 0x20000000;
 95       *  uint32 internal constant MASK_FILL_OR_KILL = 0x10000000;
 96       *  uint32 internal constant MASK_KEEP_POS_LEVERAGE = 0x08000000;
 97       *  uint32 internal constant MASK_LIMIT_ORDER = 0x04000000;
 98       */
 99      function postOrder(uint24 iPerpetualId, int256 _amountDec18, int16 _leverageInteger, uint32 orderFlag) internal returns (bool) {
100          require(_leverageInteger >= 0, "invalid lvg");
101          int128 fTradeAmount = _fromDec18(_amountDec18);
102          int128 fLeverage = _fromInt(int256(_leverageInteger));
103          ID8xPerpetualsContractInterface.ClientOrder memory order;
104          order.flags = orderFlag;//MASK_MARKET_ORDER
105          order.iPerpetualId = iPerpetualId;
106          order.traderAddr = address(this);
107          order.fAmount = fTradeAmount;
108          order.fLimitPrice = fTradeAmount > 0 ? MAX_64x64 : int128(0);
109          order.fLeverage = fLeverage; // 0 if deposit and trade separate
110          order.iDeadline = uint64(block.timestamp + 86400 * 3);
111          order.createdTimestamp = uint64(block.timestamp);
112          // fields not required:
113          //      uint16 brokerFeeTbps;
114          //      address brokerAddr;
115          //      address referrerAddr;
116          //      bytes brokerSignature;
117          //      int128 fTriggerPrice;
118          //      bytes32 parentChildDigest1;
119          //      bytes32 parentChildDigest2;
120  
121          // submit order
122          try ID8xPerpetualsContractInterface(orderBookAddr).postOrder(order, bytes("")) {
123              emit PerpOrderSubmitSuccess(_amountDec18, _leverageInteger);
124              return true;
125          } catch Error(string memory reason) {
126              emit PerpOrderSubmitFailed(reason);
127              return false;
128          }
129      }
130  
131      /**
132       * Return margin account information in decimal 18 format
133       */
134      function getMarginAccount(uint24 iPerpetualId) internal view returns (ID8xPerpetualsContractInterface.D18MarginAccount memory) {
135          ID8xPerpetualsContractInterface.MarginAccount memory acc = ID8xPerpetualsContractInterface(perpetualProxy).getMarginAccount(
136              iPerpetualId,
137              address(this)
138          );
139          ID8xPerpetualsContractInterface.D18MarginAccount memory accD18;
140          accD18.lockedInValueQCD18 = toDec18(acc.fLockedInValueQC); // unrealized value locked-in when trade occurs: price * position size
141          accD18.cashCCD18 = toDec18(acc.fCashCC); // cash in collateral currency (base, quote, or quanto)
142          accD18.positionSizeBCD18 = toDec18(acc.fPositionBC); // position in base currency (e.g., 1 BTC for BTCUSD)
143          accD18.positionId = acc.positionId; // unique id for the position (for given trader, and perpetual).
144          return accD18;
145      }
146  
147      /**
148       * Get maximal trade amount for the contract accounting for its current position
149       * @param isBuy true if we go long, false if we go short
150       * @return signed maximal trade size (negative if resulting position is short, positive otherwise)
151       */
152      function getMaxTradeAmount(uint24 iPerpetualId, bool isBuy) internal view returns (int256) {
153          ID8xPerpetualsContractInterface.MarginAccount memory acc = ID8xPerpetualsContractInterface(perpetualProxy).getMarginAccount(
154              iPerpetualId,
155              address(this)
156          );
157          int128 fSize = ID8xPerpetualsContractInterface(perpetualProxy).getMaxSignedOpenTradeSizeForPos(
158              iPerpetualId,
159              acc.fPositionBC,
160              isBuy
161          );
162  
163          if ((isBuy && fSize < 0) || (!isBuy && fSize > 0)) {
164              // obsolete with deployment past April 23
165              fSize = 0;
166          }
167  
168          return toDec18(fSize);
169      }
170  
171      function getAllowedStables() public view returns (address[] memory) {
172          address[] memory allowedTokens = settings.getAllowedTokens();
173          uint8 d8xPoolCount = ID8xPerpetualsContractInterface(perpetualProxy).getPoolCount();
174          address[] memory outTokens = new address[](allowedTokens.length);
175          uint256 foundCount  = 0;
176          for (uint256 i=0;i<allowedTokens.length;i++){
177              for (uint8 j=1; j<=d8xPoolCount; j++){
178                  ID8xPerpetualsContractInterface.LiquidityPoolData[] memory d8xPoolData = ID8xPerpetualsContractInterface(perpetualProxy).getLiquidityPools(j, j);
179                   if (allowedTokens[i] == d8xPoolData[0].marginTokenAddress) {
180                      outTokens[i] = allowedTokens[i];
181                      foundCount++;
182                      continue;
183                  }
184              }
185          }
186  
187          address[] memory outTokensReal = new address[](foundCount);
188  
189          uint rIdx = 0;
190          for (uint i=0; i<allowedTokens.length; i++) {
191              if (outTokens[i] != address(0)) {
192                  outTokensReal[rIdx] = outTokens[i];
193                  rIdx++;
194              }
195          }
196  
197          return outTokensReal;
198      }
199  
200      function getAssetIdsForUnderlying(string memory underlyingStr, address allowedToken) private view returns (uint24) {
201  
202          uint8 d8xPoolCount = ID8xPerpetualsContractInterface(perpetualProxy).getPoolCount();
203  
204          for (uint8 j=1; j<=d8xPoolCount; j++){
205              ID8xPerpetualsContractInterface.LiquidityPoolData[] memory d8xPoolData = ID8xPerpetualsContractInterface(perpetualProxy).getLiquidityPools(j, j);
206              uint8 d8xPoolDataPerpCount = ID8xPerpetualsContractInterface(perpetualProxy).getPerpetualCountInPool(j);
207              for (uint8 k = 0; k < d8xPoolDataPerpCount; k++){
208                  uint24 tPerpId = ID8xPerpetualsContractInterface(perpetualProxy).getPerpetualId(j, k);
209                  (bytes32[] memory d8xAssetIds, ) = ID8xPerpetualsContractInterface(perpetualProxy).getPriceInfo(tPerpId);
210                  bool foundId = findAllowedUnderlying(underlyingStr, d8xAssetIds);
211  
212                  if ((allowedToken == d8xPoolData[0].marginTokenAddress) && (foundId == true)) {
213                      return tPerpId;
214                  } 
215              }
216          }
217      }
218  
219      function getPosSize(address underlying, bool isLong) override public view returns (uint[] memory) {
220          address[] memory allowedTokens = getAllowedStables();
221          uint256[] memory posData = new uint256[](allowedTokens.length);
222          return posData;
223      }
224  
225      function getPosSize(string memory underlyingStr, bool isLong) public view returns (int256[] memory, uint24[] memory) {
226          address[] memory allowedTokens = getAllowedStables();
227          int256[] memory posSize = new int256[](allowedTokens.length);
228          uint24[] memory perIds = new uint24[](allowedTokens.length);
229  
230          for (uint i=0; i<allowedTokens.length; i++) {
231              uint24 d8xPerpId = getAssetIdsForUnderlying(underlyingStr, allowedTokens[i]);
232              ID8xPerpetualsContractInterface.D18MarginAccount memory accD18 = getMarginAccount(d8xPerpId);
233  
234              posSize[i] = accD18.positionSizeBCD18;
235              perIds[i] = d8xPerpId;
236          }
237  
238  
239          return (posSize, perIds);
240      }
241  
242      function getMaxPosSize(string memory underlyingStr, bool isLong) public view returns (int256) {
243          address[] memory allowedTokens = getAllowedStables();
244          int256[] memory posData = new int256[](allowedTokens.length);
245  
246          for (uint i=0; i<allowedTokens.length; i++) {
247              
248              uint24 d8xPerpId = getAssetIdsForUnderlying(underlyingStr, allowedTokens[i]);
249              posData[i] = getMaxTradeAmount(d8xPerpId, isLong);
250          }
251  
252          int256 totalExposure = 0;
253          for (uint i=0; i<(allowedTokens.length); i++) {
254              totalExposure = totalExposure.add(posData[i]);
255          }
256  
257          return totalExposure;
258      }
259  
260      function getHedgeExposure(address underlying) override public view returns (int256) {
261          return 0;
262      }
263  
264      function getHedgeExposure(string memory underlyingStr) public view returns (int256) {
265          address[] memory allowedTokens = getAllowedStables();
266          int256[] memory posData = new int256[](allowedTokens.length);
267  
268          for (uint i=0; i<allowedTokens.length; i++) {
269              
270              uint24 d8xPerpId = getAssetIdsForUnderlying(underlyingStr, allowedTokens[i]);
271              ID8xPerpetualsContractInterface.D18MarginAccount memory accD18 = getMarginAccount(d8xPerpId);
272              posData[i] = accD18.positionSizeBCD18;
273          }
274  
275          int256 totalExposure = 0;
276          for (uint i=0; i<(allowedTokens.length); i++) {
277              totalExposure = totalExposure.add(posData[i]);
278          }
279  
280          return totalExposure;
281      }
282      
283  
284      function idealHedgeExposure(address underlying) override public view returns (int256) {
285          // 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)
286          (,address[] memory _tokens, uint[] memory _holding,, uint[] memory _uncovered,, address[] memory _underlying) = exchange.getBook(poolAddr);
287  
288          int totalDelta = 0;
289          for (uint i = 0; i < _tokens.length; i++) {
290              address _tk = _tokens[i];
291              IOptionsExchange.OptionData memory opt = exchange.getOptionData(_tk);
292              if (_underlying[i] == underlying){
293                  int256 delta;
294  
295                  if ((_uncovered[i] > 0) && (_uncovered[i] > _holding[i])) {
296                      // net short this option, thus does not need to be modified
297                      delta = ICollateralManager(
298                          settings.getUdlCollateralManager(opt.udlFeed)
299                      ).calcDelta(
300                          opt,
301                          _uncovered[i].sub(_holding[i])
302                      );
303                  }  
304  
305  
306                  if (_holding[i] > 0){
307                      // net long thus needs to multiply by -1
308                      delta = ICollateralManager(
309                          settings.getUdlCollateralManager(opt.udlFeed)
310                      ).calcDelta(
311                          opt,
312                          _holding[i]
313                      ).mul(-1);
314                  }
315  
316                  totalDelta = totalDelta.add(delta);
317              }
318          }
319          return totalDelta;
320      }
321      
322      function realHedgeExposure(address udlFeedAddr) override public view returns (int256) {
323          // look at metavault exposure for underlying, and divide by asset price
324          (, int256 udlPrice) = UnderlyingFeed(udlFeedAddr).getLatestPrice();
325          string memory underlyingStr = AggregatorV3Interface(UnderlyingFeed(udlFeedAddr).getUnderlyingAggAddr()).description();
326  
327          int256 exposure = getHedgeExposure(underlyingStr);
328          return exposure.mul(int(_volumeBase)).div(udlPrice);
329      }
330      
331      function balanceExposure(address udlFeedAddr) override external returns (bool) {
332          ExposureData memory exData;
333          exData.underlying = UnderlyingFeed(udlFeedAddr).getUnderlyingAddr();
334          exData.underlyingStr = AggregatorV3Interface(UnderlyingFeed(udlFeedAddr).getUnderlyingAggAddr()).description();
335          (, int256 udlPrice) = UnderlyingFeed(udlFeedAddr).getLatestPrice();
336          exData.udlPrice = uint256(udlPrice);
337          exData.allowedTokens = getAllowedStables();
338          exData.totalStables = creditProvider.totalTokenStock();
339          exData.totalHedgingStables = totalTokenStock();
340          exData.poolLeverage = (settings.isAllowedCustomPoolLeverage(poolAddr) == true) ? IGovernableLiquidityPool(poolAddr).getLeverage() : defaultLeverage;
341          require(exData.poolLeverage <= maxLeverage && exData.poolLeverage >= minLeverage, "leverage out of range");
342          exData.ideal = idealHedgeExposure(exData.underlying);
343          exData.real = getHedgeExposure(exData.underlyingStr).mul(int(_volumeBase)).div(udlPrice);
344          exData.diff = exData.ideal.sub(exData.real);
345  
346          //dont bother to hedge if delta is below $ val threshold
347          if (uint256(MoreMath.abs(exData.diff)).mul(exData.udlPrice).div(_volumeBase) < IGovernableLiquidityPool(poolAddr).getHedgeNotionalThreshold()) {
348              return false;
349          }
350  
351  
352          //close out existing open pos
353          if (exData.real != 0) {
354              //need to close long position first
355              //need to loop over all available exchange stablecoins, or need to deposit underlying int to vault (if there is a vault for it)
356              (exData.openPos, exData.perpIds) = getPosSize(exData.underlyingStr, true);
357              for(uint i=0; i< exData.openPos.length; i++){
358                  if (exData.openPos[i] != 0) {
359                      postOrder(exData.perpIds[i], exData.openPos[i], 0, 0x80000000);
360                  }
361              }
362              
363  
364              if (exData.real > 0) {
365                  exData.pos_size = uint256(MoreMath.abs(exData.ideal));
366              }
367  
368              if (exData.real < 0) {
369                  exData.pos_size = uint256(exData.ideal);
370              }
371          }
372  
373          //open new pos
374          if (exData.ideal <= 0) {
375              // increase short position by pos_size
376              if (exData.pos_size != 0) {
377                  exData.totalPosValue = exData.pos_size.mul(exData.udlPrice).div(_volumeBase);
378                  exData.totalPosValueToTransfer = exData.totalPosValue.div(exData.poolLeverage);
379  
380                  require(
381                      getMaxShortLiquidity(udlFeedAddr) >= exData.totalPosValue,
382                      "no short hedge liq"
383                  );
384  
385                  // hedging should fail if not enough stables in exchange
386                  if (exData.totalStables.mul(exData.poolLeverage) > exData.totalPosValue) {
387                      for (uint i=0; i< exData.allowedTokens.length; i++) {
388  
389                          if (exData.totalPosValueToTransfer > 0) {
390                              exData.t = IERC20_2(exData.allowedTokens[i]);
391                              
392                              (exData.r, exData.b) = settings.getTokenRate(exData.allowedTokens[i]);
393                              if (exData.b != 0) {
394                                  uint v = MoreMath.min(
395                                      exData.totalPosValueToTransfer, 
396                                      exData.t.balanceOf(address(creditProvider)).mul(exData.b).div(exData.r)
397                                  );
398  
399                                  //.mul(b).div(r); //convert to exchange decimals
400  
401                                  if (exData.t.allowance(address(this), perpetualProxy) > 0) {
402                                      exData.t.safeApprove(perpetualProxy, 0);
403                                  }
404                                  exData.t.safeApprove(perpetualProxy, v.mul(exData.r).div(exData.b));
405  
406                                  //transfer collateral from credit provider to hedging manager and debit pool bal
407                                  exData.at = new address[](1);
408                                  exData.at[0] = exData.allowedTokens[i];
409  
410                                  exData.tv = new uint[](1);
411                                  exData.tv[0] = v;
412  
413  
414                                  if (exData.totalHedgingStables < exData.totalPosValueToTransfer){
415                                      ICollateralManager(
416                                          settings.getUdlCollateralManager(
417                                              udlFeedAddr
418                                          )
419                                      ).borrowTokensByPreference(
420                                          address(this), poolAddr, v, exData.at, exData.tv
421                                      );
422                                  }
423  
424                                  v = v.mul(exData.r).div(exData.b);//converts to token decimals
425  
426                                  uint24 d8xPerpId = getAssetIdsForUnderlying(exData.underlyingStr, exData.allowedTokens[i]);
427                                  postOrder(d8xPerpId, int256(v.mul(exData.r).div(exData.b)).mul(-1), int16(exData.poolLeverage), 0x40000000);
428  
429                                  //back to exchange decimals
430  
431                                  if (exData.totalPosValueToTransfer > v.mul(exData.r).div(exData.b)) {
432                                      exData.totalPosValueToTransfer = exData.totalPosValueToTransfer.sub(v.mul(exData.r).div(exData.b));
433  
434                                  } else {
435                                      exData.totalPosValueToTransfer = 0;
436                                  }
437  
438                                  exData.r = 0;
439                                  exData.b = 0;
440                              }                            
441                          }
442                      }
443                  }
444  
445                  return true;
446              }
447          } else if (exData.ideal > 0) {
448  
449              // increase long position by pos_size
450              if (exData.pos_size != 0) {
451                  exData.totalPosValue = exData.pos_size.mul(exData.udlPrice).div(_volumeBase);
452                  exData.totalPosValueToTransfer = exData.totalPosValue.div(exData.poolLeverage);
453  
454                  require(
455                      getMaxLongLiquidity(udlFeedAddr) >= exData.totalPosValue,
456                      "no long hedge liq"
457                  );
458  
459                  // hedging should fail if not enough stables in exchange
460                  if (exData.totalStables.mul(exData.poolLeverage) > exData.totalPosValue) {
461                      for (uint i=0; i< exData.allowedTokens.length; i++) {
462  
463                          if (exData.totalPosValueToTransfer > 0) {
464                              exData.t = IERC20_2(exData.allowedTokens[i]);
465                              
466                              (exData.r, exData.b) = settings.getTokenRate(exData.allowedTokens[i]);
467                              if (exData.b != 0) {
468                                  uint v = MoreMath.min(
469                                      exData.totalPosValueToTransfer,
470                                      exData.t.balanceOf(address(creditProvider)).mul(exData.b).div(exData.r)
471                                  );
472                                  if (exData.t.allowance(address(this), perpetualProxy) > 0) {
473                                      exData.t.safeApprove(perpetualProxy, 0);
474                                  }
475                                  exData.t.safeApprove(perpetualProxy, v.mul(exData.r).div(exData.b));
476  
477                                  //transfer collateral from credit provider to hedging manager and debit pool bal
478                                  exData.at = new address[](1);
479                                  address[] memory at_s = new address[](2);
480                                  exData.at[0] = exData.allowedTokens[i];
481                                  
482                                  at_s[0] = exData.allowedTokens[i];
483                                  at_s[1] = exData.underlying;
484  
485                                  exData.tv = new uint[](1);
486                                  exData.tv[0] = v;
487  
488                                  if (exData.totalHedgingStables < exData.totalPosValueToTransfer){
489                                      ICollateralManager(
490                                          settings.getUdlCollateralManager(
491                                              udlFeedAddr
492                                          )
493                                      ).borrowTokensByPreference(
494                                          address(this), poolAddr, v, exData.at, exData.tv
495                                      );
496                                  }
497  
498                                  v = v.mul(exData.r).div(exData.b);//converts to token decimals
499  
500  
501                                  uint24 d8xPerpId = getAssetIdsForUnderlying(exData.underlyingStr, exData.allowedTokens[i]);
502                                  postOrder(d8xPerpId, int256(v.mul(exData.r).div(exData.b)), int16(exData.poolLeverage), 0x40000000);
503  
504                                  //back to exchange decimals
505                                  if (exData.totalPosValueToTransfer > v.mul(exData.r).div(exData.b)) {
506                                      exData.totalPosValueToTransfer = exData.totalPosValueToTransfer.sub(v.mul(exData.r).div(exData.b));
507  
508                                  } else {
509                                      exData.totalPosValueToTransfer = 0;
510                                  }
511                                  exData.r = 0;
512                                  exData.b = 0;
513                              }                             
514                          }
515                      }
516                  }
517  
518                  return true;
519              }
520          }
521  
522          return false;
523      }
524  
525      //TODO: ask about how to get maxmium size avaialble to trade for an account, and my account existing pos size for a pool
526  
527      function getMaxLongLiquidity(address udlFeedAddr) public view returns (uint v) {
528          ExposureData memory exData;
529          exData.underlyingStr = AggregatorV3Interface(UnderlyingFeed(udlFeedAddr).getUnderlyingAggAddr()).description();
530  
531          return uint256(getMaxPosSize(exData.underlyingStr, true));
532  
533      }
534  
535      function getMaxShortLiquidity(address udlFeedAddr) public view returns (uint v) {
536          ExposureData memory exData;
537          exData.underlyingStr = AggregatorV3Interface(UnderlyingFeed(udlFeedAddr).getUnderlyingAggAddr()).description();
538  
539          return uint256(MoreMath.abs(getMaxPosSize(exData.underlyingStr, false)));
540          
541      }
542  
543      function totalTokenStock() override public view returns (uint v) {
544  
545          address[] memory tokens = getAllowedStables();
546          for (uint i = 0; i < tokens.length; i++) {
547              (uint r, uint b) = settings.getTokenRate(tokens[i]);
548              uint value = IERC20_2(tokens[i]).balanceOf(address(this));
549              v = v.add(value.mul(b).div(r));
550          }
551      }
552  
553      /**
554       * Convert signed decimal-18 number to ABDK-128x128 format
555       * @param x number decimal-18
556       * @return ABDK-128x128 number
557       */
558      function _fromDec18(int256 x) internal pure returns (int128) {
559          int256 result = (x * ONE_64x64) / DECIMALS;
560          require(x >= MIN_64x64 && x <= MAX_64x64, "result out of range");
561          return int128(result);
562      }
563  
564      /**
565       * Convert ABDK-128x128 format to signed decimal-18 number
566       * @param x number in ABDK-128x128 format
567       * @return decimal 18 (signed)
568       */
569      function toDec18(int128 x) internal pure returns (int256) {
570          return (int256(x) * DECIMALS) / ONE_64x64;
571      }
572  
573      /**
574       * Convert signed 256-bit integer number into signed 64.64-bit fixed point
575       * number.  Revert on overflow.
576       *
577       * @param x signed 256-bit integer number
578       * @return signed 64.64-bit fixed point number
579       */
580      function _fromInt(int256 x) internal pure returns (int128) {
581          require(x >= -0x8000000000000000 && x <= 0x7FFFFFFFFFFFFFFF, "ABDK.fromInt");
582          return int128(x << 64);
583      }
584  
585      function transferTokensToCreditProvider(address tokenAddr) override external {
586          //this needs to be used if/when liquidations happen and tokens sent from external contracts end up here
587          uint value = IERC20_2(tokenAddr).balanceOf(address(this));
588          if (value > 0) {
589              IERC20_2(tokenAddr).safeTransfer(address(creditProvider), value);
590              creditProvider.creditPoolBalance(poolAddr, tokenAddr, value);
591          }
592      }
593  
594      function findAllowedUnderlying(string memory underlyingStr, bytes32[] memory d8xAssetIds) private pure returns (bool){
595  
596          for (uint i = 0; i < d8xAssetIds.length; i++) {
597              if(keccak256(abi.encodePacked((underlyingStr))) == keccak256(abi.encodePacked((bytes32ToString(d8xAssetIds[i]))))) {
598                  return true;
599              }
600          }
601  
602          return false;
603      }
604  
605      function bytes32ToString(bytes32 x) private pure returns (string memory) {
606          bytes memory bytesString = new bytes(32);
607          uint charCount = 0;
608          for (uint j = 0; j < 32; j++) {
609              byte char = byte(bytes32(uint(x) * 2 ** (8 * j)));
610              if (char != 0) {
611                  bytesString[charCount] = char;
612                  charCount++;
613              }
614          }
615          bytes memory bytesStringTrimmed = new bytes(charCount);
616          for (uint j = 0; j < charCount; j++) {
617              bytesStringTrimmed[j] = bytesString[j];
618          }
619          return string(bytesStringTrimmed);
620      }
621  
622      function toAsciiString(address x) internal pure returns (string memory) {
623          bytes memory s = new bytes(40);
624          for (uint i = 0; i < 20; i++) {
625              bytes1 b = bytes1(uint8(uint(uint160(x)) / (2**(8*(19 - i)))));
626              bytes1 hi = bytes1(uint8(b) / 16);
627              bytes1 lo = bytes1(uint8(b) - 16 * uint8(hi));
628              s[2*i] = char(hi);
629              s[2*i+1] = char(lo);            
630          }
631          return string(s);
632      }
633  
634      function char(bytes1 b) internal pure returns (bytes1 c) {
635          if (uint8(b) < 10) return bytes1(uint8(b) + 0x30);
636          else return bytes1(uint8(b) + 0x57);
637      }
638  }