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 }