/ contracts / pools / GovernableLiquidityPoolV2.sol
GovernableLiquidityPoolV2.sol
  1  pragma solidity >=0.6.0;
  2  pragma experimental ABIEncoderV2;
  3  
  4  import "../deployment/ManagedContract.sol";
  5  import "../interfaces/IOptionToken.sol";
  6  import "../interfaces/IProposal.sol";
  7  import "../interfaces/IGovernableLiquidityPool.sol";
  8  import "../interfaces/IYieldTracker.sol";
  9  import "../interfaces/UnderlyingFeed.sol";
 10  import "../interfaces/ICreditProvider.sol";
 11  import "../interfaces/IProtocolSettings.sol";
 12  import "../interfaces/IProposalWrapper.sol";
 13  import "../interfaces/IProposalManager.sol";
 14  import "../interfaces/IInterpolator.sol";
 15  import "../interfaces/IBaseHedgingManager.sol";
 16  import "../finance/RedeemableToken.sol";
 17  import "../utils/SafeERC20.sol";
 18  import "../utils/SafeCast.sol";
 19  import "../utils/MoreMath.sol";
 20  import "../utils/SignedSafeMath.sol";
 21  
 22  abstract contract GovernableLiquidityPoolV2 is ManagedContract, RedeemableToken, IGovernableLiquidityPool {
 23  
 24      using SafeERC20 for IERC20_2;
 25      using SafeCast for uint;
 26      using SafeMath for uint;
 27      using SignedSafeMath for int;
 28  
 29      string internal _name;
 30      string internal _symbol;
 31      string internal constant _symbol_prefix = "DODv2-LLPRTK-";
 32      string internal constant _name_prefix = "Linear Liquidity Pool Redeemable Token: ";
 33  
 34      IYieldTracker private tracker;
 35      IProtocolSettings private settings;
 36      IInterpolator internal interpolator;
 37      ICreditProvider private creditProvider;
 38      IProposalManager private proposalManager;
 39  
 40      mapping(string => PricingParameters) private parameters;
 41      mapping(string => mapping(uint => Range)) private ranges;
 42  
 43      uint public override maturity;
 44      uint public override withdrawFee;
 45      uint internal volumeBase = 1e18;
 46      uint internal reserveRatio;
 47      uint internal fractionBase;
 48      uint internal _leverageMultiplier;
 49      uint internal _hedgeThreshold;
 50  
 51      address private _hedgingManagerAddress;
 52      bool private onlyMintToOwner;
 53      
 54      string[] private optSymbols;
 55  
 56      constructor(string memory _nm, string memory _sb, address _deployAddr, bool _onlyMintToOwner, address _owner)
 57          ERC20(string(abi.encodePacked(_name_prefix, _nm)))
 58          public
 59      {    
 60          _symbol = _sb;
 61          _name = _nm;
 62  
 63          fractionBase = 1e9;
 64          exchange = IOptionsExchange(Deployer(_deployAddr).getContractAddress("OptionsExchange"));
 65          settings = IProtocolSettings(Deployer(_deployAddr).getContractAddress("ProtocolSettings"));
 66          creditProvider = ICreditProvider(Deployer(_deployAddr).getContractAddress("CreditProvider"));
 67          tracker = IYieldTracker(Deployer(_deployAddr).getContractAddress("YieldTracker"));
 68          interpolator = IInterpolator(Deployer(_deployAddr).getContractAddress("Interpolator"));
 69          proposalManager = IProposalManager(Deployer(_deployAddr).getContractAddress("ProposalsManager"));
 70          owner = _owner;
 71          onlyMintToOwner = _onlyMintToOwner;
 72      }
 73  
 74      function setParameters(
 75          uint _reserveRatio,
 76          uint _withdrawFee,
 77          uint _mt,
 78          uint _lm,
 79          address _hmngr,
 80          uint _ht
 81      )
 82          override external
 83      {
 84          ensureCaller();
 85          reserveRatio = _reserveRatio;
 86          withdrawFee = _withdrawFee;
 87          maturity = _mt;
 88          _leverageMultiplier = _lm;
 89          _hedgingManagerAddress = _hmngr;
 90          _hedgeThreshold = _ht;
 91      }
 92  
 93      function getHedgingManager() override public view returns (address) {
 94          return _hedgingManagerAddress;
 95      }
 96  
 97      function getHedgeNotionalThreshold() override external view returns (uint) {
 98          return _hedgeThreshold;
 99      }
100  
101      function getLeverage() override public view returns (uint) {
102          return _leverageMultiplier;
103      }
104  
105      function redeemAllowed() override public view returns (bool) {
106          
107          return block.timestamp >= maturity; //FOR DEPLOYMENTS
108          //return settings.exchangeTime() >= maturity;//FOR TESTS
109      }
110  
111      function yield(uint dt) override external view returns (uint) {
112          return tracker.yield(address(this), dt);
113      }
114      
115      function addSymbol(
116          address udlFeed,
117          uint strike,
118          uint _mt,
119          IOptionsExchange.OptionType optType,
120          uint t0,
121          uint t1,
122          uint120[] calldata x,
123          uint120[] calldata y,
124          uint[3] calldata bsStockSpread
125      )
126          override external
127      {
128          ensureCaller();
129          require(x.length > 0 && x.length.mul(2) == y.length && _mt < maturity, "bad x/y or _mt");
130  
131          string memory optSymbol = exchange.getOptionSymbol(
132              IOptionsExchange.OptionData(udlFeed, optType, strike.toUint120(), _mt.toUint32())
133          );
134  
135          if (parameters[optSymbol].x.length == 0) {
136              optSymbols.push(optSymbol);
137          }
138  
139          parameters[optSymbol] = PricingParameters(
140              udlFeed,
141              optType,
142              strike.toUint120(),
143              _mt.toUint32(),
144              t0.toUint32(),
145              t1.toUint32(),
146              bsStockSpread,
147              x,
148              y
149          );
150  
151          emit AddSymbol(optSymbol);
152      }
153  
154      function setRange(string calldata optSymbol, Operation op, uint start, uint end) external {
155          ensureCaller();
156          ranges[optSymbol][uint(op)] = Range(start.toUint120(), end.toUint120());
157      }
158  
159      function removeSymbol(string calldata optSymbol) external {
160          require(parameters[optSymbol].maturity >= block.timestamp, "2 soon");        
161          Arrays.removeItem(optSymbols, optSymbol);
162      }
163  
164      function depositTokens(address to, address token, uint value) override public {
165  
166          if (onlyMintToOwner) {
167              require(to == owner, "bad ownr");
168          }
169  
170          (uint b0, int po) = getBalanceAndPayout();
171          depositTokensInExchange(token, value);
172          uint b1 = exchange.balanceOf(address(this));
173          
174          //tracker.push(int(b0).add(po), b1.sub(b0).toInt256());
175  
176          int expBal = po.add(int(b1));
177          uint p = b1.sub(b0).mul(fractionBase).div(uint(expBal));
178  
179          uint b = 1e3;
180          uint v = _totalSupply > 0 ?
181              _totalSupply.mul(p).mul(b).div(fractionBase.sub(p)) : 
182              uint(expBal).mul(b);
183          v = MoreMath.round(v, b);
184  
185          addBalance(to, v);
186          _totalSupply = _totalSupply.add(v);
187          emitTransfer(address(0), to, v);
188      }
189  
190      function withdraw(uint amount) override external {
191  
192          uint bal = balanceOf(msg.sender);
193          require(bal >= amount, "low bal");
194  
195          uint val = valueOf(msg.sender).mul(amount).div(bal);
196          uint discountedValue = val.mul(fractionBase.sub(withdrawFee)).div(fractionBase);
197          uint freeBal = calcFreeBalance();
198  
199          if (freeBal > 0) {
200              //(uint b0, int po) = getBalanceAndPayout();
201              
202              exchange.transferBalance(
203                  msg.sender, 
204                  (discountedValue <= freeBal) ? discountedValue : freeBal
205              );
206              
207              /*tracker.push(
208                  int(b0).add(po), 
209                  -(((discountedValue <= freeBal) ? val : freeBal).toInt256())
210              );*/
211          }
212          
213          removeBalance(msg.sender, amount);
214          _totalSupply = _totalSupply.sub(amount);
215          emitTransfer(msg.sender, address(0), amount);
216      }
217  
218      function calcFreeBalance() override public view returns (uint balance) {
219          //used for pool deposits/withdrawls of pool tokens
220          uint exBal = exchange.balanceOf(address(this));
221          uint reserve = exBal.mul(reserveRatio).div(fractionBase);
222          uint sp = exBal.sub(exchange.collateral(address(this)));
223          balance = sp > reserve ? sp.sub(reserve) : 0;
224      }
225  
226      function calcFreeTradableBalance() internal view returns (uint balance) {
227          //used for calcing what traders can trade against
228          uint exBal = settings.getPoolCreditTradeable(address(this));
229          uint reserve = exBal.mul(reserveRatio).div(fractionBase);
230          uint sp = exBal.sub(exchange.collateral(address(this)));
231          balance = sp > reserve ? sp.sub(reserve) : 0;
232      }
233  
234      function listSymbols() override external view returns (string memory available) {
235          for (uint i = 0; i < optSymbols.length; i++) {
236              if (bytes(available).length == 0) {
237                  available = optSymbols[i];
238              } else {
239                  available = string(abi.encodePacked(available, "\n", optSymbols[i]));
240              }
241          }
242      }
243  
244      function queryBuy(string memory optSymbol, bool isBuy)
245          override
246          public
247          view
248          returns (uint price, uint volume)
249      {
250  
251          Operation op = (isBuy == true) ? Operation.BUY : Operation.SELL;
252  
253          PricingParameters memory param = parameters[optSymbol];
254          address _tk = exchange.resolveToken(optSymbol);
255          uint optBal = (op == Operation.SELL) ? IOptionToken(_tk).balanceOf(address(this)) : IOptionToken(_tk).writtenVolume(address(this));
256          price = calcOptPrice(param, op, IOptionToken(_tk).balanceOf(address(this)), IOptionToken(_tk).writtenVolume(address(this)));
257          volume = MoreMath.min(
258              calcVolume(optSymbol, param, price, op, 0),
259              (op == Operation.SELL) ? (
260                  (param.bsStockSpread[1] >= optBal) ? param.bsStockSpread[1].sub(optBal) : 0
261              ) : (
262              (param.bsStockSpread[0] >= optBal) ? param.bsStockSpread[0].sub(optBal) : 0
263              )
264          );
265      }
266  
267      function buy(string memory optSymbol, uint price, uint volume, address token)
268          override
269          public
270          returns (address _tk)
271      {
272          PricingParameters memory param;
273          _tk = exchange.resolveToken(optSymbol);
274  
275          (price, param)  = validateOrder(volume, price, optSymbol, Operation.BUY, _tk);
276          checkApproved(param.udlFeed);
277  
278          uint value = price.mul(volume).div(volumeBase);
279          if (token != address(exchange)) {
280              (uint tv, uint tb) = settings.getTokenRate(token);
281              value = value.mul(tv).div(tb);
282              depositTokensInExchange(token, value);
283          } else {
284              exchange.transferBalance(msg.sender, address(this), value);
285          }
286  
287  
288          if (volume > IOptionToken(_tk).balanceOf(address(this))) {
289              // only credit the amount excess what is already available
290              uint freeBal = calcFreeBalance();
291              if (value > freeBal){
292                  creditProvider.borrowBuyLiquidity(address(this), value.sub(freeBal), _tk);
293              }
294              writeOptions(_tk, param, volume, msg.sender);
295          } else {
296              IOptionToken(_tk).transfer(msg.sender, volume);
297          }
298  
299          hedge(param);
300  
301          emit Buy(_tk, msg.sender, price, volume);
302      }
303  
304      function sell(
305          string memory optSymbol,
306          uint price,
307          uint volume
308      )
309          override
310          public
311      {
312          PricingParameters memory param;
313          address _tk = exchange.resolveToken(optSymbol);
314          (price, param) = validateOrder(volume, price, optSymbol, Operation.SELL, _tk);
315          checkApproved(param.udlFeed);
316  
317          IOptionToken(_tk).transferFrom(msg.sender, address(this), volume);
318          
319          uint _written = IOptionToken(_tk).writtenVolume(address(this));
320          if (_written > 0) {
321              IOptionToken(_tk).burn(
322                  MoreMath.min(_written, volume)
323              );
324          }
325  
326          uint value = price.mul(volume).div(volumeBase);
327          uint freeBal = calcFreeBalance();
328          if (freeBal < value) {
329              // only credit the amount excess what is already available, will fail if hedging manager not approved
330              creditProvider.borrowSellLiquidity(address(this), value.sub(freeBal), _tk); 
331          }
332               
333          exchange.transferBalance(msg.sender, value);
334          // holding <= sellStock
335          require(calcFreeBalance() > 0 && IOptionToken(_tk).balanceOf(address(this)) <= param.bsStockSpread[1], "bal 2 low/high volume");
336  
337          hedge(param);
338  
339          emit Sell(_tk, msg.sender, price, volume);
340      }
341  
342      function getBalanceAndPayout() private view returns (uint bal, int pOut) {
343          
344          bal = exchange.balanceOf(address(this));
345          pOut = exchange.calcExpectedPayout(address(this));
346      }
347  
348      function hedge(PricingParameters memory param) private {
349          //trigger hedge, may need to factor in costs and charge it to msg.sender
350          if (_hedgingManagerAddress != address(0)) {
351              IBaseHedgingManager(_hedgingManagerAddress).balanceExposure(
352                  param.udlFeed
353              );
354          }
355      }
356  
357      function isInRange(
358          string memory optSymbol,
359          Operation op,
360          address udlFeed
361      )
362          private
363          view
364          returns(bool)
365      {
366          Range memory r = ranges[optSymbol][uint(op)];
367          if (r.start == 0 && r.end == 0) {
368              return true;
369          }
370          int udlPrice = getUdlPrice(udlFeed);
371          return uint(udlPrice) >= r.start && uint(udlPrice) <= r.end;
372      }
373  
374      function validateOrder(
375          uint volume,
376          uint price, 
377          string memory optSymbol, 
378          Operation op,
379          address _tk
380      ) 
381          private
382          view
383          returns (uint p, PricingParameters memory param) 
384      {
385          param = parameters[optSymbol];
386          require(volume > 0 && isInRange(optSymbol, op, param.udlFeed), "bad rng or vol");
387          p = calcOptPrice(
388              param,
389              op,
390              IOptionToken(_tk).balanceOf(address(this)),
391              IOptionToken(_tk).writtenVolume(address(this))
392          );
393          require(
394              op == Operation.BUY ? price >= p : price <= p,
395              "bad price"
396          );
397      }
398  
399      function valueOf(address ownr) override public view returns (uint) {
400          (uint bal, int pOut) = getBalanceAndPayout();
401          return uint(int(bal).add(pOut))
402              .mul(balanceOf(ownr)).div(totalSupply());
403      }
404      
405      function writeOptions(
406          address _tk,
407          PricingParameters memory param,
408          uint volume,
409          address to
410      )
411          virtual
412          internal;
413  
414      function calcOptPrice(PricingParameters memory p, Operation op, uint poolPosBuy, uint poolPosSell)
415          virtual
416          internal
417          view
418          returns (uint price);
419  
420      function calcVolume(
421          string memory optSymbol,
422          PricingParameters memory p,
423          uint price,
424          Operation op,
425          uint poolPos
426      )
427          virtual
428          internal
429          view
430          returns (uint volume);
431  
432      function getUdlPrice(address udlFeed) internal view returns (int udlPrice) {
433          (, udlPrice) = UnderlyingFeed(udlFeed).getLatestPrice();
434      }
435  
436      function depositTokensInExchange(address token, uint value) private {
437          IERC20_2(token).safeTransferFrom(msg.sender, address(this), value);
438          IERC20_2(token).safeApprove(address(exchange), value);
439          exchange.depositTokens(address(this), token, value);
440      }
441  
442      function ensureCaller() private view {
443          require(proposalManager.isRegisteredProposal(msg.sender) && IProposalWrapper(proposalManager.resolve(msg.sender)).isPoolSettingsAllowed(), "ONG");
444      }
445  
446      function checkApproved(address udlFeed) private view {
447          address ppk = UnderlyingFeed(udlFeed).getPrivledgedPublisherKeeper();
448          if (ppk != address(0)) {
449              require(ppk == tx.origin, "not approved");
450          }
451      }
452  }