/ contracts / feeds / DEXOracleV1.sol
DEXOracleV1.sol
  1  /*
  2      Copyright 2021 DeFi Options DAO, based on the works of the Empty Set Squad
  3  
  4      Licensed under the Apache License, Version 2.0 (the "License");
  5      you may not use this file except in compliance with the License.
  6      You may obtain a copy of the License at
  7  
  8      http://www.apache.org/licenses/LICENSE-2.0
  9  
 10      Unless required by applicable law or agreed to in writing, software
 11      distributed under the License is distributed on an "AS IS" BASIS,
 12      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13      See the License for the specific language governing permissions and
 14      limitations under the License.
 15  */
 16  
 17  pragma solidity >=0.6.0;
 18  pragma experimental ABIEncoderV2;
 19  
 20  import "@pangolindex/exchange-contracts/contracts/pangolin-core/interfaces/IPangolinFactory.sol";
 21  import "@pangolindex/exchange-contracts/contracts/pangolin-core/interfaces/IPangolinPair.sol";
 22  import "../deployment/Deployer.sol";
 23  import "../utils/PangolinOracleLibrary.sol";
 24  import "../utils/PangolinLibrary.sol";
 25  import "../utils/Decimal.sol";
 26  import "../interfaces/IDEXOracleV1.sol";
 27  import "../interfaces/IProposal.sol";
 28  import "../interfaces/IProtocolSettings.sol";
 29  
 30  contract DEXOracleV1 is IDEXOracleV1 {
 31      using Decimal for Decimal.D256;
 32  
 33      address private _pairAddr;
 34      address private _exchange;
 35      address private _settings;
 36      address private _underlying;
 37      address private _stablecoin;
 38  
 39      uint256 private serial;
 40      uint256 private _index;
 41      uint256 private _reserve;
 42      uint256 private _cumulative;
 43      uint256 private _lastCapture;
 44      uint256 private _twapPeriodDefault = 60 * 60 * 24; // 1 day
 45  
 46  
 47      bool private _latestValid;
 48      bool private _initialized;
 49  
 50      uint32 private _timestamp;
 51      int256 private _latestPrice;
 52  
 53  
 54      IPangolinPair private _pair;
 55  
 56      mapping(address => uint) private proposingId;
 57      mapping(uint => address) private proposalsMap;
 58  
 59      constructor (address _deployAddr, address underlying, address stable, address dexTokenPair) public {
 60  
 61          Deployer deployer = Deployer(_deployAddr);
 62  
 63          _exchange = deployer.getContractAddress("OptionsExchange");
 64          _settings = deployer.getContractAddress("ProtocolSettings");
 65          _underlying = underlying;
 66          _stablecoin = stable;
 67          _pairAddr = dexTokenPair;
 68  
 69          (uint r, uint b) = IProtocolSettings(_settings).getTokenRate(_stablecoin);
 70          require(r != 0 && b != 0, "DEXOracleV1: token not allowed");
 71          
 72          _pair = IPangolinPair(_pairAddr);
 73          (address token0, address token1) = (_pair.token0(), _pair.token1());
 74          _index = _underlying == token0 ? 0 : 1;
 75          require(_index == 0 || _underlying == token1, "DEXOracleV1: Underlying not found");
 76      }
 77  
 78      /**
 79       * Trades/Liquidity: (1) Initializes reserve and blockTimestampLast (can calculate a price)
 80       *                   (2) Has non-zero cumulative prices
 81       *
 82       * Steps: (1) Captures a reference blockTimestampLast
 83       *        (2) First reported value
 84       */
 85      function capture() override public onlyExchange returns (int256, bool) {
 86          uint256 currentTime = IProtocolSettings(_settings).exchangeTime();
 87          uint256 _twapPeriod = IProtocolSettings(_settings).getDexOracleTwapPeriod(address(this));
 88  
 89          if (_lastCapture != 0) {
 90              require(
 91                  SafeMath.sub(
 92                      currentTime,
 93                      _lastCapture
 94                  ) >= ((_twapPeriod == 0) ? _twapPeriodDefault : _twapPeriod), "DEXOracleV1: too soon"
 95              );
 96          }
 97  
 98          if (_initialized) {
 99              _lastCapture = currentTime;
100              return updateOracle();
101          } else {
102              initializeOracle();
103              _lastCapture = currentTime;
104              return updateOracle();
105          }
106      }
107  
108      function initializeOracle() private {
109          IPangolinPair pair = _pair;
110          uint256 priceCumulative = _index == 0 ?
111              pair.price0CumulativeLast() :
112              pair.price1CumulativeLast();
113          (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = pair.getReserves();
114          if(reserve0 != 0 && reserve1 != 0 && blockTimestampLast != 0) {
115              _cumulative = priceCumulative;
116              _timestamp = blockTimestampLast;
117              _initialized = true;
118              _reserve = _index == 0 ? reserve1 : reserve0; // get counter's reserve
119          }
120      }
121  
122      function updateOracle() private returns (int256, bool) {
123          int256 price = updatePrice();
124          updateReserve();
125  
126          bool valid = true;
127  
128          if (price < 1e8) {
129              valid = false;
130          }
131  
132          _latestValid = valid;
133          _latestPrice = price;
134  
135          return (price, valid);
136      }
137  
138      function updatePrice() private returns (int256) {
139          (uint256 price0Cumulative, uint256 price1Cumulative, uint32 blockTimestamp) =
140          PangolinOracleLibrary.currentCumulativePrices(address(_pair));
141          uint32 timeElapsed = blockTimestamp - _timestamp; // overflow is desired
142          uint256 priceCumulative = _index == 0 ? price0Cumulative : price1Cumulative;
143          Decimal.D256 memory price = Decimal.ratio((priceCumulative - _cumulative) / timeElapsed, 2**112);
144  
145          _timestamp = blockTimestamp;
146          _cumulative = priceCumulative;
147  
148          return int256(price.mul(1e8).asUint256());
149      }
150  
151      function updateReserve() private returns (uint256) {
152          uint256 lastReserve = _reserve;
153          (uint112 reserve0, uint112 reserve1,) = _pair.getReserves();
154          _reserve = _index == 0 ? reserve1 : reserve0; // get counter's reserve
155  
156          return lastReserve;
157      }
158  
159      function liveReserve() override external view returns (uint256) {
160          (uint112 reserve0, uint112 reserve1,) = _pair.getReserves();
161          uint256 lastReserve = _index == 0 ? reserve1 : reserve0; // get counter's reserve
162  
163          return lastReserve;
164      }
165  
166      function stablecoin() override external view returns (address) {
167          return _stablecoin;
168      }
169  
170      function pair() override external view returns (address) {
171          return _pairAddr;
172      }
173  
174      function latestPrice() override public view returns (int256) {
175          return _latestPrice;
176      }
177  
178      function latestValid() override public view returns (bool) {
179          return _latestValid;
180      }
181  
182      function latestCapture() override public view returns (uint256) {
183          return _lastCapture;
184      }
185  
186      modifier onlyExchange() {
187          require(msg.sender == _exchange, "DEXOracleV1: Not exchange");
188  
189          _;
190      }
191  }