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 }