TombBasedOracles
25 removals
105 lines
20 additions
104 lines
// SPDX-License-Identifier: MIT
// SPDX-License-Identifier: MIT
pragma solidity 0.6.12;
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "./lib/Babylonian.sol";
import "./lib/Babylonian.sol";
import "./lib/FixedPoint.sol";
import "./lib/FixedPoint.sol";
import "./lib/UniswapV2OracleLibrary.sol";
import "./lib/UniswapV2OracleLibrary.sol";
import "./utils/Epoch.sol";
import "./utils/Epoch.sol";
import "./interfaces/IUniswapV2Pair.sol";
import "./interfaces/IUniswapV2Pair.sol";
/*
/*
______ __ _______
__________ .___ ___________.__
/_ __/___ ____ ___ / /_ / ____(_)___ ____ _____ ________
\______ \_____ ______ ____ __| _/ \_ _____/|__| ____ _____ ____ ____ ____
/ / / __ \/ __ `__ \/ __ \ / /_ / / __ \/ __ `/ __ \/ ___/ _ \
| | _/\__ \ / ___/_/ __ \ / __ | | __) | | / \ \__ \ / \ _/ ___\_/ __ \
/ / / /_/ / / / / / / /_/ / / __/ / / / / / /_/ / / / / /__/ __/
| | \ / __ \_ \___ \ \ ___/ / /_/ | | \ | || | \ / __ \_| | \\ \___\ ___/
/_/ \____/_/ /_/ /_/_.___/ /_/ /_/_/ /_/\__,_/_/ /_/\___/\___/
|______ /(____ //____ > \___ >\____ | \___ / |__||___| /(____ /|___| / \___ >\___ >
\/ \/ \/ \/ \/ \/ \/ \/ \/ \/ \/
http://tomb.finance
*/
*/
// fixed window oracle that recomputes the average price for the entire period once every period
// fixed window oracle that recomputes the average price for the entire period once every period
// note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period
// note that the price average is only guaranteed to be over at least 1 period, but may be over a longer period
contract Oracle is Epoch {
contract Oracle is Epoch {
using FixedPoint for *;
using FixedPoint for *;
using SafeMath for uint256;
using SafeMath for uint256;
/* ========== STATE VARIABLES ========== */
/* ========== STATE VARIABLES ========== */
// uniswap
// uniswap
address public token0;
address public token0;
address public token1;
address public token1;
IUniswapV2Pair public pair;
IUniswapV2Pair public pair;
// oracle
// oracle
uint32 public blockTimestampLast;
uint32 public blockTimestampLast;
uint256 public price0CumulativeLast;
uint256 public price0CumulativeLast;
uint256 public price1CumulativeLast;
uint256 public price1CumulativeLast;
FixedPoint.uq112x112 public price0Average;
FixedPoint.uq112x112 public price0Average;
FixedPoint.uq112x112 public price1Average;
FixedPoint.uq112x112 public price1Average;
/* ========== CONSTRUCTOR ========== */
/* ========== CONSTRUCTOR ========== */
constructor(
constructor(
IUniswapV2Pair _pair,
IUniswapV2Pair _pair,
uint256 _period,
uint256 _period,
uint256 _startTime
uint256 _startTime
) public Epoch(_period, _startTime, 0) {
) Epoch(_period, _startTime, 0) {
pair = _pair;
pair = _pair;
token0 = pair.token0();
token0 = pair.token0();
token1 = pair.token1();
token1 = pair.token1();
price0CumulativeLast = pair.price0CumulativeLast(); // fetch the current accumulated price value (1 / 0)
price0CumulativeLast = pair.price0CumulativeLast(); // fetch the current accumulated price value (1 / 0)
price1CumulativeLast = pair.price1CumulativeLast(); // fetch the current accumulated price value (0 / 1)
price1CumulativeLast = pair.price1CumulativeLast(); // fetch the current accumulated price value (0 / 1)
uint112 reserve0;
uint112 reserve0;
uint112 reserve1;
uint112 reserve1;
(reserve0, reserve1, blockTimestampLast) = pair.getReserves();
(reserve0, reserve1, blockTimestampLast) = pair.getReserves();
require(reserve0 != 0 && reserve1 != 0, "Oracle: NO_RESERVES"); // ensure that there's liquidity in the pair
require(reserve0 != 0 && reserve1 != 0, "Oracle: NO_RESERVES"); // ensure that there's liquidity in the pair
}
}
/* ========== MUTABLE FUNCTIONS ========== */
/* ========== MUTABLE FUNCTIONS ========== */
/** @dev Updates 1-day EMA price from Uniswap. */
/** @dev Updates 1-day EMA price from Uniswap. */
function update() external checkEpoch {
function update() external checkEpoch {
(uint256 price0Cumulative, uint256 price1Cumulative, uint32 blockTimestamp) = UniswapV2OracleLibrary.currentCumulativePrices(address(pair));
(uint256 price0Cumulative, uint256 price1Cumulative, uint32 blockTimestamp) = UniswapV2OracleLibrary.currentCumulativePrices(address(pair));
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
if (timeElapsed == 0) {
if (timeElapsed == 0) {
// prevent divided by zero
// prevent divided by zero
return;
return;
}
}
// overflow is desired, casting never truncates
// overflow is desired, casting never truncates
// cumulative price is in (uq112x112 price * seconds) units so we simply wrap it after division by time elapsed
// cumulative price is in (uq112x112 price * seconds) units so we simply wrap it after division by time elapsed
price0Average = FixedPoint.uq112x112(uint224((price0Cumulative - price0CumulativeLast) / timeElapsed));
price0Average = FixedPoint.uq112x112(uint224((price0Cumulative - price0CumulativeLast) / timeElapsed));
price1Average = FixedPoint.uq112x112(uint224((price1Cumulative - price1CumulativeLast) / timeElapsed));
price1Average = FixedPoint.uq112x112(uint224((price1Cumulative - price1CumulativeLast) / timeElapsed));
price0CumulativeLast = price0Cumulative;
price0CumulativeLast = price0Cumulative;
price1CumulativeLast = price1Cumulative;
price1CumulativeLast = price1Cumulative;
blockTimestampLast = blockTimestamp;
blockTimestampLast = blockTimestamp;
emit Updated(price0Cumulative, price1Cumulative);
emit Updated(price0Cumulative, price1Cumulative);
}
}
// note this will always return 0 before update has been called successfully for the first time.
// note this will always return 0 before update has been called successfully for the first time.
function consult(address _token, uint256 _amountIn) external view returns (uint144 amountOut) {
function consult(address _token, uint256 _amountIn) external view returns (uint144 amountOut) {
if (_token == token0) {
if (_token == token0) {
amountOut = price0Average.mul(_amountIn).decode144();
amountOut = price0Average.mul(_amountIn).decode144();
} else {
} else {
require(_token == token1, "Oracle: INVALID_TOKEN");
require(_token == token1, "Oracle: INVALID_TOKEN");
amountOut = price1Average.mul(_amountIn).decode144();
amountOut = price1Average.mul(_amountIn).decode144();
}
}
}
}
function twap(address _token, uint256 _amountIn) external view returns (uint144 _amountOut) {
function twap(address _token, uint256 _amountIn) external view returns (uint144 _amountOut) {
(uint256 price0Cumulative, uint256 price1Cumulative, uint32 blockTimestamp) = UniswapV2OracleLibrary.currentCumulativePrices(address(pair));
(uint256 price0Cumulative, uint256 price1Cumulative, uint32 blockTimestamp) = UniswapV2OracleLibrary.currentCumulativePrices(address(pair));
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
if (_token == token0) {
if (_token == token0) {
_amountOut = FixedPoint.uq112x112(uint224((price0Cumulative - price0CumulativeLast) / timeElapsed)).mul(_amountIn).decode144();
_amountOut = FixedPoint.uq112x112(uint224((price0Cumulative - price0CumulativeLast) / timeElapsed)).mul(_amountIn).decode144();
} else if (_token == token1) {
} else if (_token == token1) {
_amountOut = FixedPoint.uq112x112(uint224((price1Cumulative - price1CumulativeLast) / timeElapsed)).mul(_amountIn).decode144();
_amountOut = FixedPoint.uq112x112(uint224((price1Cumulative - price1CumulativeLast) / timeElapsed)).mul(_amountIn).decode144();
}
}
}
}
event Updated(uint256 price0CumulativeLast, uint256 price1CumulativeLast);
event Updated(uint256 price0CumulativeLast, uint256 price1CumulativeLast);
}
}