/ contracts / finance / UnderlyingVault.sol
UnderlyingVault.sol
  1  pragma solidity >=0.6.0;
  2  
  3  import "../deployment/ManagedContract.sol";
  4  import "../interfaces/IProtocolSettings.sol";
  5  import "../interfaces/IERC20.sol";
  6  import "../interfaces/IERC20Details.sol";
  7  import "../interfaces/IUniswapV2Router01.sol";
  8  import "../interfaces/TimeProvider.sol";
  9  import "../interfaces/UnderlyingFeed.sol";
 10  import "../interfaces/ICreditProvider.sol";
 11  import "../interfaces/IUnderlyingCreditProvider.sol";
 12  import "../utils/Convert.sol";
 13  import "../utils/MoreMath.sol";
 14  import "../utils/SafeERC20.sol";
 15  
 16  contract UnderlyingVault is ManagedContract {
 17  
 18      using SafeERC20 for IERC20_2;
 19      using SafeMath for uint;
 20      using SignedSafeMath for int;
 21  
 22      uint private fractionBase = 1e9;
 23  
 24      TimeProvider private time;
 25      IProtocolSettings private settings;
 26      ICreditProvider private creditProvider;
 27      
 28      mapping(address => uint) private callers;
 29      mapping(address => mapping(address => uint)) private _totalSupplyRehypothicated;//token-> protocol->amount
 30      mapping(address => mapping(address => uint)) private _totalSupplyShareRehypothicated;//token share-> protocol->amount
 31      mapping(address => bool) private _isRehypothicate;
 32      mapping(address => mapping(address => uint)) private allocation;
 33      mapping(address => mapping(address => mapping(address => uint))) private _rehypothecationAllocation;//token->protocol->user->amount
 34      mapping(address => mapping(address => address)) private _activeUserRehypothicationProtocol;//token->user->active protocol
 35  
 36      mapping(address => uint256) _totalSupply;
 37      mapping(address => address) _underlyingCreditProvider;
 38  
 39      struct tsVars {
 40          uint balR;
 41          uint valR;
 42          uint rBalR;
 43      }
 44  
 45  
 46      event Lock(address indexed owner, address indexed token, uint value);
 47  
 48      event Liquidate(address indexed owner, address indexed token, uint valueIn, uint valueOut);
 49  
 50      event Release(address indexed owner, address indexed token, uint value);
 51  
 52      function initialize(Deployer deployer) override internal {
 53  
 54          time = TimeProvider(deployer.getContractAddress("TimeProvider"));
 55          settings = IProtocolSettings(deployer.getContractAddress("ProtocolSettings"));
 56          creditProvider = ICreditProvider(deployer.getContractAddress("CreditProvider"));
 57          
 58          callers[deployer.getContractAddress("OptionsExchange")] = 1;
 59          callers[deployer.getContractAddress("CollateralManager")] = 1;
 60          callers[deployer.getContractAddress("Incentivized")] = 1;
 61          callers[address(settings)] = 1;
 62      }
 63  
 64  
 65      function setup(Deployer deployer) public {
 66  
 67          time = TimeProvider(deployer.getContractAddress("TimeProvider"));
 68          settings = IProtocolSettings(deployer.getContractAddress("ProtocolSettings"));
 69          creditProvider = ICreditProvider(deployer.getContractAddress("CreditProvider"));
 70          
 71          callers[deployer.getContractAddress("OptionsExchange")] = 1;
 72          callers[deployer.getContractAddress("CollateralManager")] = 1;
 73          callers[deployer.getContractAddress("Incentivized")] = 1;
 74          callers[address(settings)] = 1;
 75      }
 76  
 77      function balanceOf(address owner, address token) public view returns (uint) {
 78  
 79          return allocation[owner][token];
 80      }
 81  
 82      function totalSupply(address token) public view returns (uint) {
 83          return _totalSupply[token];
 84      }
 85  
 86      function getUnderlyingCreditProvider(address token) public view returns (address) {
 87          return _underlyingCreditProvider[token];
 88      }
 89  
 90      function setUnderlyingCreditProvider(address token, address udlCreditProviderAddress) external {
 91          ensureCaller();
 92          require(udlCreditProviderAddress != address(0), "bad udlcdprov");
 93          require(_underlyingCreditProvider[token] == address(0), "existing udlcdprov");
 94          _underlyingCreditProvider[token] = udlCreditProviderAddress;
 95      }
 96  
 97      function balanceOfRehypothicatedShares(address owner, address token, address rehypothicationManager) public view returns (uint) {
 98          return _rehypothecationAllocation[token][rehypothicationManager][owner];
 99      }
100  
101      function addUnderlyingShareBalanceRehypothicated(address owner, address token, address rehypothicationManager, uint v) private {
102          _rehypothecationAllocation[token][rehypothicationManager][owner] = _rehypothecationAllocation[token][rehypothicationManager][owner].add(v);
103          _totalSupplyShareRehypothicated[token][rehypothicationManager] = _totalSupplyShareRehypothicated[token][rehypothicationManager].add(v);
104      }
105  
106      function removeUnderlyingSharesBalanceRehypothicated(address owner, address token, address rehypothicationManager, uint v) private {
107          _rehypothecationAllocation[token][rehypothicationManager][owner] = _rehypothecationAllocation[token][rehypothicationManager][owner].sub(v);
108          _totalSupplyShareRehypothicated[token][rehypothicationManager] = _totalSupplyShareRehypothicated[token][rehypothicationManager].sub(v);
109      }
110  
111      function totalSupplyRehypothicated(address token, address rehypothicationManager) public view returns (uint) {
112          return _totalSupplyRehypothicated[token][rehypothicationManager];
113      }
114  
115  
116      function totalSupplySharesRehypothicated(address token, address rehypothicationManager) public view returns (uint) {
117          return _totalSupplyShareRehypothicated[token][rehypothicationManager] ;
118      }
119  
120      function addUnderlyingSupplyRehypothicated(address token, address rehypothicationManager, uint value) private {
121          _totalSupplyRehypothicated[token][rehypothicationManager] = _totalSupplyRehypothicated[token][rehypothicationManager].add(value);
122      }
123  
124      function removeUnderlyingSupplyRehypothicated(address token, address rehypothicationManager, uint value) private {
125          _totalSupplyRehypothicated[token][rehypothicationManager] = _totalSupplyRehypothicated[token][rehypothicationManager].sub(value);
126      }
127  
128      function lock(address owner, address token, uint value, bool isRehypothicate, address rehypothicationManager) external {
129  
130          ensureCaller();
131          
132          require(owner != address(0), "invalid owner");
133          require(token != address(0), "invalid token");
134  
135          allocation[owner][token] = allocation[owner][token].add(value);
136          _totalSupply[token] = _totalSupply[token].add(value);
137  
138          if (isRehypothicate == true) {
139              _isRehypothicate[owner] = true;
140  
141              require(settings.isAllowedRehypothicationManager(rehypothicationManager) == true, "rehyM not allowed");
142  
143              if (_activeUserRehypothicationProtocol[token][owner] != rehypothicationManager){
144                  //needs to be null if diff
145                  require(_activeUserRehypothicationProtocol[token][owner] == address(0), "rehyM already in use");
146              } else {
147                  //is null addr, set
148                  _activeUserRehypothicationProtocol[token][owner] = rehypothicationManager;
149              }
150  
151              uint b0 = totalSupplyRehypothicated(token, rehypothicationManager);
152              addUnderlyingSupplyRehypothicated(token, rehypothicationManager, value);
153              //transfer tokens for rehypothication to credit provider
154              IUnderlyingCreditProvider(_underlyingCreditProvider[token]).depositTokens(
155                  owner,
156                  token,
157                  Convert.from18DecimalsBase(token, value)
158              );
159              uint b1 = totalSupplyRehypothicated(token, rehypothicationManager);
160              uint p = b1.sub(b0).mul(fractionBase).div(b1);
161              uint b = 1e3;
162              uint v = totalSupplySharesRehypothicated(token, rehypothicationManager) > 0 ?
163                  totalSupplySharesRehypothicated(token, rehypothicationManager).mul(p).mul(b).div(fractionBase.sub(p)) : 
164                  b1.mul(b);
165              v = MoreMath.round(v, b);
166  
167              addUnderlyingShareBalanceRehypothicated(owner, token, rehypothicationManager, v);
168              
169          }
170  
171          emit Lock(owner, token, value);
172      }
173  
174      function liquidate(
175          address owner,
176          address token,
177          address feed,
178          uint amountOut
179      )
180          external
181          returns (uint _in, uint _out)
182      {
183          ensureCaller();
184          
185          require(owner != address(0), "invalid owner");
186          require(token != address(0), "invalid token");
187          require(feed != address(0), "invalid feed");
188  
189  
190          uint balance = balanceOf(owner, token);
191  
192          if (balance > 0) {
193  
194              (address _router, address _stablecoin) = settings.getSwapRouterInfo();
195              require(
196                  _router != address(0) && _stablecoin != address(0),
197                  "invalid swap router settings"
198              );
199  
200              (, int p) = UnderlyingFeed(feed).getLatestPrice();
201  
202              //NEED TO DO THIS BEFORE CALLING `swapUnderlyingForStablecoin` in order to make sure enough token in vault (by transfering all of the users balance to vault, then depositing back to user what was liquidated)
203              uint udlValB = IUnderlyingCreditProvider(_underlyingCreditProvider[token]).balanceOf(owner);
204              IUnderlyingCreditProvider(_underlyingCreditProvider[token]).grantTokens(address(this), udlValB);
205  
206              (_in, _out) = swapUnderlyingForStablecoin(
207                  owner,
208                  IUniswapV2Router01(_router),
209                  settings.getSwapPath(
210                      token,
211                      _stablecoin
212                  ),
213                  p,
214                  balance,
215                  amountOut
216              );
217  
218              uint udlValO = udlValB.sub(_in);
219              allocation[owner][token] = allocation[owner][token].sub(_in);
220              _totalSupply[token] = _totalSupply[token].sub(_in);
221  
222              //send residual back to cdtp
223              if (udlValO > 0) {
224                  IERC20_2(token).safeTransfer(_underlyingCreditProvider[token], Convert.from18DecimalsBase(token, udlValO));
225              }
226  
227              if (_isRehypothicate[owner] == true){
228                  address rehypothicationManager = _activeUserRehypothicationProtocol[token][owner];
229  
230                  if (rehypothicationManager != address(0)) {
231                      tsVars memory tsv;
232                      tsv.balR = balanceOfRehypothicatedShares(owner, token, rehypothicationManager);
233                      tsv.valR = valueOfRehypothicatedShares(owner, token, rehypothicationManager);
234                      tsv.rBalR = _in.mul(tsv.balR).div(tsv.valR);
235  
236                      removeUnderlyingSharesBalanceRehypothicated(owner, token, rehypothicationManager, tsv.rBalR);
237                      removeUnderlyingSupplyRehypothicated(token, rehypothicationManager, _in);
238                      checkAndResetRehypothicationManager(owner, token, rehypothicationManager);
239                  }
240              }
241  
242              emit Liquidate(owner, token, _in, _out);
243          }
244      }
245  
246      function valueOfRehypothicatedShares(address ownr, address token, address rehypothicationManager) public view returns (uint) {
247          uint bal = _totalSupplyShareRehypothicated[token][rehypothicationManager];
248          uint balOwnr = balanceOfRehypothicatedShares(ownr, token, rehypothicationManager);
249          return uint(int(bal))
250              .mul(balOwnr).div(_totalSupplyRehypothicated[token][rehypothicationManager]);
251      }
252  
253      function release(address owner, address token, address feed, uint value) external {
254          
255          ensureCaller();
256          
257          require(owner != address(0), "invalid owner");
258          require(token != address(0), "invalid token");
259          require(feed != address(0), "invalid feed");
260  
261          uint bal = allocation[owner][token];
262          value = MoreMath.min(bal, value);
263  
264          if (bal > 0) {
265  
266              allocation[owner][token] = bal.sub(value);
267              _totalSupply[token] = _totalSupply[token].sub(value);
268  
269              if (_isRehypothicate[owner] == true){
270                  address rehypothicationManager = _activeUserRehypothicationProtocol[token][owner];
271  
272                  if (rehypothicationManager != address(0)) {
273                      uint balR = balanceOfRehypothicatedShares(owner, token, rehypothicationManager);
274                      value = valueOfRehypothicatedShares(owner, token, rehypothicationManager);
275                      removeUnderlyingSharesBalanceRehypothicated(owner, token, rehypothicationManager, balR);
276                      removeUnderlyingSupplyRehypothicated(token, rehypothicationManager, value);
277                      checkAndResetRehypothicationManager(owner, token, rehypothicationManager);
278                  }
279  
280                  IUnderlyingCreditProvider(_underlyingCreditProvider[token]).withdrawTokens(owner, value);
281              } else {
282                  address underlying = UnderlyingFeed(feed).getUnderlyingAddr();
283                  uint v = Convert.from18DecimalsBase(underlying, value);
284                  IERC20_2(underlying).safeTransfer(owner, v);
285              }
286              
287              emit Release(owner, token, value);
288          }
289      }
290  
291      function checkAndResetRehypothicationManager(address owner, address token, address rehypothicationManager) private {
292          uint balR = balanceOfRehypothicatedShares(owner, token, rehypothicationManager);
293          if (balR == 0) {
294              //unset rehyM
295              _activeUserRehypothicationProtocol[token][owner] = address(0);
296          }
297      }
298  
299      function swapUnderlyingForStablecoin(
300          address owner,
301          IUniswapV2Router01 router,
302          address[] memory path,
303          int price,
304          uint balance,
305          uint amountOut
306      )
307          private
308          returns (uint _in, uint _out)
309      {
310          require(path.length >= 2, "invalid swap path");
311          
312          (uint r, uint b) = settings.getTokenRate(path[path.length - 1]);
313  
314          uint udlBalance = Convert.from18DecimalsBase(path[0], balance);
315          
316          uint amountInMax = getAmountInMax(
317              price,
318              amountOut,
319              path
320          );
321  
322          if (amountInMax > udlBalance) {
323              amountOut = amountOut.mul(udlBalance).div(amountInMax);
324              amountInMax = udlBalance;
325          }
326  
327          IERC20_2 tk = IERC20_2(path[0]);
328          if (tk.allowance(address(this), address(router)) > 0) {
329              tk.safeApprove(address(router), 0);
330          }
331          tk.safeApprove(address(router), amountInMax);
332  
333          _out = amountOut;
334          _in = router.swapTokensForExactTokens(
335              amountOut.mul(r).div(b),
336              amountInMax,
337              path,
338              address(creditProvider),
339              time.getNow()
340          )[0];
341          _in = Convert.to18DecimalsBase(path[0], _in);
342  
343          if (amountOut > 0) {
344              creditProvider.addBalance(owner, path[path.length - 1], amountOut.mul(r).div(b));
345          }
346      }
347  
348      function getAmountInMax(
349          int price,
350          uint amountOut,
351          address[] memory path
352      )
353          private
354          view
355          returns (uint amountInMax)
356      {
357          uint8 d = IERC20Details(path[0]).decimals();
358          amountInMax = amountOut.mul(10 ** uint(d)).div(uint(price));
359          
360          (uint rTol, uint bTol) = settings.getSwapRouterTolerance();
361          amountInMax = amountInMax.mul(rTol).div(bTol);
362      }
363  
364      function getAmountInMaxInv(
365          int price,
366          uint amountOut,
367          address[] memory path
368      )
369          public
370          view
371          returns (uint amountInMax)
372      {
373          uint8 d = IERC20Details(path[0]).decimals();
374          amountInMax = amountOut.mul(10 ** uint(d)).mul(uint(price));
375          
376          (uint rTol, uint bTol) = settings.getSwapRouterTolerance();
377          amountInMax = amountInMax.mul(rTol).div(bTol);
378      }
379  
380      function ensureCaller() private view {
381          
382          require(callers[msg.sender] == 1, "Vault: unauthorized caller");
383      }
384  }