InternalHedgingManager.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/IBaseRehypothecationManager.sol"; 9 import "../../interfaces/AggregatorV3Interface.sol"; 10 import "../../interfaces/UnderlyingFeed.sol"; 11 import "../../interfaces/IUnderlyingVault.sol"; 12 import "../../interfaces/IInternalHedgingManagerFactory.sol"; 13 import "../../interfaces/IUnderlyingCreditProvider.sol"; 14 import "../../utils/Convert.sol"; 15 16 17 contract InternalHedgingManager is BaseHedgingManager { 18 address private internalRehypothecationAddr; 19 address private internalHedgingManagerFactoryAddr; 20 uint private maxLeverage = 30; 21 uint private minLeverage = 1; 22 uint private defaultLeverage = 15; 23 uint constant _volumeBase = 1e18; 24 25 IUnderlyingVault vault; 26 27 struct ExposureData { 28 IERC20_2 t; 29 30 int256 diff; 31 int256 real; 32 int256 ideal; 33 34 uint256 pos_size; 35 uint256 udlPrice; 36 uint256 poolLeverage; 37 uint256 totalPosValue; 38 uint256 totalPosValueToTransfer; 39 40 address underlying; 41 address udlCdtP; 42 address udlCdtk; 43 44 address[] at; 45 address[] allowedTokens; 46 uint256[] tv; 47 uint256[] openPos; 48 } 49 50 constructor(address _deployAddr, address _poolAddr) public { 51 //TODO: need to make sure proper addrs are available 52 poolAddr = _poolAddr; 53 Deployer deployer = Deployer(_deployAddr); 54 super.initialize(deployer); 55 internalHedgingManagerFactoryAddr = deployer.getContractAddress("InternalHedgingManagerFactory"); 56 address _internalRehypothecationAddr = IInternalHedgingManagerFactory(internalHedgingManagerFactoryAddr).getRemoteContractAddresses(); 57 vault = IUnderlyingVault(deployer.getContractAddress("UnderlyingVault")); 58 require(_internalRehypothecationAddr != address(0), "bad order book"); 59 60 internalRehypothecationAddr = _internalRehypothecationAddr; 61 } 62 63 function pool() override external view returns (address) { 64 return poolAddr; 65 } 66 67 function getAllowedStables() public view returns (address[] memory) { 68 address[] memory outTokensReal = new address[](1); 69 outTokensReal[0] = address(exchange);//exchnage balance will be used as stable, udlcredit tokens will be used as underlying 70 return outTokensReal; 71 } 72 73 74 function getPosSize(address underlying, bool isLong) override public view returns (uint[] memory) { 75 address udlCdtP = vault.getUnderlyingCreditProvider(underlying); 76 address udlCdtk = IUnderlyingCreditProvider(udlCdtP).getUnderlyingCreditToken(); 77 78 79 address[] memory allowedTokens = getAllowedStables(); 80 uint256[] memory posSize = new uint256[](allowedTokens.length); 81 82 if (isLong == true) { 83 //(asset == exchange balance, collateral == udl credit) 84 posSize[0] = IBaseRehypothecationManager(internalRehypothecationAddr).notionalExposure(address(this), address(exchange), udlCdtk); 85 } else { 86 //(collateral == exchange balance, asset == udl credit) 87 posSize[0] = IBaseRehypothecationManager(internalRehypothecationAddr).notionalExposure(address(this), udlCdtk, address(exchange)); 88 } 89 90 return posSize; 91 } 92 93 function getHedgeExposure(address underlying) override public view returns (int256) { 94 address udlCdtP = vault.getUnderlyingCreditProvider(underlying); 95 address udlCdtk = IUnderlyingCreditProvider(udlCdtP).getUnderlyingCreditToken(); 96 int256 totalExposure = 0; 97 //(asset == exchange balance, collateral == udl credit) 98 totalExposure = totalExposure.add(int256(IBaseRehypothecationManager(internalRehypothecationAddr).notionalExposure(address(this), address(exchange), udlCdtk))); 99 //(collateral == exchange balance, asset == udl credit) 100 totalExposure = totalExposure.sub(int256(IBaseRehypothecationManager(internalRehypothecationAddr).notionalExposure(address(this), udlCdtk, address(exchange)))); 101 return totalExposure; 102 } 103 104 function idealHedgeExposure(address underlying) override public view returns (int256) { 105 // 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) 106 (,address[] memory _tokens, uint[] memory _holding,, uint[] memory _uncovered,, address[] memory _underlying) = exchange.getBook(poolAddr); 107 108 int totalDelta = 0; 109 for (uint i = 0; i < _tokens.length; i++) { 110 address _tk = _tokens[i]; 111 IOptionsExchange.OptionData memory opt = exchange.getOptionData(_tk); 112 if (_underlying[i] == underlying){ 113 int256 delta; 114 115 if ((_uncovered[i] > 0) && (_uncovered[i] > _holding[i])) { 116 // net short this option, thus does not need to be modified 117 delta = ICollateralManager( 118 settings.getUdlCollateralManager(opt.udlFeed) 119 ).calcDelta( 120 opt, 121 _uncovered[i].sub(_holding[i]) 122 ); 123 } 124 125 126 if (_holding[i] > 0){ 127 // net long thus needs to multiply by -1 128 delta = ICollateralManager( 129 settings.getUdlCollateralManager(opt.udlFeed) 130 ).calcDelta( 131 opt, 132 _holding[i] 133 ).mul(-1); 134 } 135 136 totalDelta = totalDelta.add(delta); 137 } 138 } 139 return totalDelta; 140 } 141 142 function realHedgeExposure(address udlFeedAddr) override public view returns (int256) { 143 // look at metavault exposure for underlying, and divide by asset price 144 (, int256 udlPrice) = UnderlyingFeed(udlFeedAddr).getLatestPrice(); 145 address underlying = UnderlyingFeed(udlFeedAddr).getUnderlyingAddr(); 146 147 int256 exposure = getHedgeExposure(underlying); 148 return exposure.mul(int(_volumeBase)).div(udlPrice); 149 } 150 151 function balanceExposure(address udlFeedAddr) override external returns (bool) { 152 ExposureData memory exData; 153 exData.underlying = UnderlyingFeed(udlFeedAddr).getUnderlyingAddr(); 154 exData.udlCdtP = vault.getUnderlyingCreditProvider(exData.underlying); 155 exData.udlCdtk = IUnderlyingCreditProvider(exData.udlCdtP).getUnderlyingCreditToken(); 156 (, int256 udlPrice) = UnderlyingFeed(udlFeedAddr).getLatestPrice(); 157 exData.udlPrice = uint256(udlPrice); 158 exData.allowedTokens = getAllowedStables(); 159 exData.poolLeverage = (settings.isAllowedCustomPoolLeverage(poolAddr) == true) ? IGovernableLiquidityPool(poolAddr).getLeverage() : defaultLeverage; 160 require(exData.poolLeverage <= maxLeverage && exData.poolLeverage >= minLeverage, "leverage out of range"); 161 exData.ideal = idealHedgeExposure(exData.underlying); 162 exData.real = getHedgeExposure(exData.underlying).mul(int(_volumeBase)).div(udlPrice); 163 exData.diff = exData.ideal.sub(exData.real); 164 165 //dont bother to hedge if delta is below $ val threshold 166 if (uint256(MoreMath.abs(exData.diff)).mul(exData.udlPrice).div(_volumeBase) < IGovernableLiquidityPool(poolAddr).getHedgeNotionalThreshold()) { 167 return false; 168 } 169 170 171 //close out existing open pos 172 if (exData.real != 0) { 173 //need to close long position first 174 //need to loop over all available exchange stablecoins, or need to deposit underlying int to vault (if there is a vault for it) 175 176 if (exData.real > 0) { 177 exData.openPos = getPosSize(exData.underlying, true); 178 for(uint i=0; i< exData.openPos.length; i++){ 179 if (exData.openPos[i] != 0) { 180 //approve && repay long 181 //(asset == exchange balance, collateral == udl credit), long 182 183 if (IERC20_2(exData.udlCdtk).allowance(address(this), internalRehypothecationAddr) > 0) { 184 IERC20_2(exData.udlCdtk).safeApprove(internalRehypothecationAddr, 0); 185 } 186 IERC20_2(exData.udlCdtk).safeApprove( 187 internalRehypothecationAddr, 188 IBaseRehypothecationManager(internalRehypothecationAddr).borrowExposure(address(this), address(exchange), exData.udlCdtk) 189 ); 190 191 IBaseRehypothecationManager(internalRehypothecationAddr).repay(address(exchange), exData.udlCdtk, udlFeedAddr); 192 } 193 } 194 exData.pos_size = uint256(MoreMath.abs(exData.ideal)); 195 } 196 197 if (exData.real < 0) { 198 exData.openPos = getPosSize(exData.underlying, false); 199 for(uint i=0; i< exData.openPos.length; i++){ 200 if (exData.openPos[i] != 0) { 201 //approve && repay short 202 //(collateral == exchange balance, asset == udl credit), short 203 if (IERC20_2(address(exchange)).allowance(address(this), internalRehypothecationAddr) > 0) { 204 IERC20_2(address(exchange)).safeApprove(internalRehypothecationAddr, 0); 205 } 206 IERC20_2(address(exchange)).safeApprove( 207 internalRehypothecationAddr, 208 IBaseRehypothecationManager(internalRehypothecationAddr).borrowExposure(address(this), exData.udlCdtk, address(exchange)) 209 ); 210 IBaseRehypothecationManager(internalRehypothecationAddr).repay(exData.udlCdtk, address(exchange), udlFeedAddr); 211 } 212 } 213 exData.pos_size = uint256(exData.ideal); 214 } 215 } 216 217 //open new pos 218 if (exData.ideal <= 0) { 219 // increase short position by pos_size 220 if (exData.pos_size != 0) { 221 exData.totalPosValue = exData.pos_size.mul(exData.udlPrice).div(_volumeBase); 222 exData.totalPosValueToTransfer = exData.totalPosValue.div(exData.poolLeverage); 223 224 for (uint i=0; i< exData.allowedTokens.length; i++) { 225 if (exData.totalPosValueToTransfer > 0) { 226 exData.t = IERC20_2(exData.allowedTokens[i]); 227 228 uint v = MoreMath.min( 229 exData.totalPosValueToTransfer, 230 exData.t.balanceOf(address(creditProvider)) 231 ); 232 233 if (exData.t.allowance(address(this), internalRehypothecationAddr) > 0) { 234 exData.t.safeApprove(internalRehypothecationAddr, 0); 235 } 236 exData.t.safeApprove(internalRehypothecationAddr, v); 237 238 //transfer collateral from credit provider to hedging manager and debit pool bal 239 exData.at = new address[](1); 240 exData.at[0] = exData.allowedTokens[i]; 241 242 exData.tv = new uint[](1); 243 exData.tv[0] = v; 244 245 ICollateralManager( 246 settings.getUdlCollateralManager( 247 udlFeedAddr 248 ) 249 ).borrowCreditFromPool( 250 address(this), poolAddr, v 251 ); 252 253 //approve collateral && lend && borrow 254 //(collateral == exchange balance, asset == udl credit), short 255 IBaseRehypothecationManager(internalRehypothecationAddr).lend(exData.udlCdtk, address(exchange), exData.pos_size, v, udlFeedAddr); 256 IBaseRehypothecationManager(internalRehypothecationAddr).borrow(exData.udlCdtk, address(exchange), exData.pos_size, v, udlFeedAddr); 257 258 if (exData.totalPosValueToTransfer > v) { 259 exData.totalPosValueToTransfer = exData.totalPosValueToTransfer.sub(v); 260 261 } else { 262 exData.totalPosValueToTransfer = 0; 263 } 264 265 } 266 } 267 268 return true; 269 } 270 } else if (exData.ideal > 0) { 271 272 // increase long position by pos_size 273 if (exData.pos_size != 0) { 274 exData.totalPosValue = exData.pos_size.mul(exData.udlPrice).div(_volumeBase); 275 exData.totalPosValueToTransfer = exData.totalPosValue.div(exData.poolLeverage); 276 277 for (uint i=0; i< exData.allowedTokens.length; i++) { 278 if (exData.totalPosValueToTransfer > 0) { 279 exData.t = IERC20_2(exData.allowedTokens[i]); 280 281 uint v = MoreMath.min( 282 exData.totalPosValueToTransfer, 283 exData.t.balanceOf(address(creditProvider)) 284 ); 285 if (exData.t.allowance(address(this), internalRehypothecationAddr) > 0) { 286 exData.t.safeApprove(internalRehypothecationAddr, 0); 287 } 288 exData.t.safeApprove(internalRehypothecationAddr, v); 289 290 //transfer collateral from credit provider to hedging manager and debit pool bal 291 exData.at = new address[](1); 292 address[] memory at_s = new address[](2); 293 exData.at[0] = exData.allowedTokens[i]; 294 295 at_s[0] = exData.allowedTokens[i]; 296 at_s[1] = exData.underlying; 297 298 exData.tv = new uint[](1); 299 exData.tv[0] = v; 300 301 ICollateralManager( 302 settings.getUdlCollateralManager( 303 udlFeedAddr 304 ) 305 ).borrowCreditFromPool( 306 address(this), poolAddr, v 307 ); 308 //approve collateral && lend && borrow 309 IBaseRehypothecationManager(internalRehypothecationAddr).lend(address(exchange), exData.udlCdtk, exData.totalPosValue, exData.pos_size.div(exData.poolLeverage), udlFeedAddr); 310 IBaseRehypothecationManager(internalRehypothecationAddr).borrow(address(exchange), exData.udlCdtk, exData.totalPosValue, exData.pos_size.div(exData.poolLeverage), udlFeedAddr); 311 312 //back to exchange decimals 313 if (exData.totalPosValueToTransfer > v) { 314 exData.totalPosValueToTransfer = exData.totalPosValueToTransfer.sub(v); 315 316 } else { 317 exData.totalPosValueToTransfer = 0; 318 } 319 } 320 } 321 322 return true; 323 } 324 } 325 326 return false; 327 } 328 329 function totalTokenStock() override public view returns (uint v) {return 0;} 330 331 function transferTokensToCreditProvider(address tokenAddr) override external {} 332 }