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