// SPDX-License-Identifier: AGPL-3.0-only // Using the same Copyleft License as in the original Repository pragma solidity 0.6.12; pragma experimental ABIEncoderV2; import "../interfaces/IOracle.sol"; import "@boringcrypto/boring-solidity/contracts/libraries/BoringMath.sol"; import "@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2Factory.sol"; import "@sushiswap/core/contracts/uniswapv2/interfaces/IUniswapV2Pair.sol"; import "../libraries/FixedPoint.sol"; // solhint-disable not-rely-on-time // adapted from https://github.com/Uniswap/uniswap-v2-periphery/blob/master/contracts/examples/ExampleSlidingWindowOracle.sol contract SimpleSLPTWAP0Oracle is IOracle { using FixedPoint for *; using BoringMath for uint256; uint256 public constant PERIOD = 5 minutes; struct PairInfo { uint256 priceCumulativeLast; uint32 blockTimestampLast; uint144 priceAverage; } mapping(IUniswapV2Pair => PairInfo) public pairs; // Map of pairs and their info mapping(address => IUniswapV2Pair) public callerInfo; // Map of callers to pairs function _get(IUniswapV2Pair pair, uint32 blockTimestamp) public view returns (uint256) { uint256 priceCumulative = pair.price0CumulativeLast(); // if time has elapsed since the last update on the pair, mock the accumulated price values (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IUniswapV2Pair(pair).getReserves(); priceCumulative += uint256(FixedPoint.fraction(reserve1, reserve0)._x) * (blockTimestamp - blockTimestampLast); // overflows ok // overflow is desired, casting never truncates // cumulative price is in (uq112x112 price * seconds) units so we simply wrap it after division by time elapsed return priceCumulative; } function getDataParameter(IUniswapV2Pair pair) public pure returns (bytes memory) { return abi.encode(pair); } // Get the latest exchange rate, if no valid (recent) rate is available, return false /// @inheritdoc IOracle function get(bytes calldata data) external override returns (bool, uint256) { IUniswapV2Pair pair = abi.decode(data, (IUniswapV2Pair)); uint32 blockTimestamp = uint32(block.timestamp); if (pairs[pair].blockTimestampLast == 0) { pairs[pair].blockTimestampLast = blockTimestamp; pairs[pair].priceCumulativeLast = _get(pair, blockTimestamp); return (false, 0); } uint32 timeElapsed = blockTimestamp - pairs[pair].blockTimestampLast; // overflow is desired if (timeElapsed < PERIOD) { return (true, pairs[pair].priceAverage); } uint256 priceCumulative = _get(pair, blockTimestamp); pairs[pair].priceAverage = FixedPoint .uq112x112(uint224((priceCumulative - pairs[pair].priceCumulativeLast) / timeElapsed)) .mul(1e18) .decode144(); pairs[pair].blockTimestampLast = blockTimestamp; pairs[pair].priceCumulativeLast = priceCumulative; return (true, pairs[pair].priceAverage); } // Check the last exchange rate without any state changes /// @inheritdoc IOracle function peek(bytes calldata data) public view override returns (bool, uint256) { IUniswapV2Pair pair = abi.decode(data, (IUniswapV2Pair)); uint32 blockTimestamp = uint32(block.timestamp); if (pairs[pair].blockTimestampLast == 0) { return (false, 0); } uint32 timeElapsed = blockTimestamp - pairs[pair].blockTimestampLast; // overflow is desired if (timeElapsed < PERIOD) { return (true, pairs[pair].priceAverage); } uint256 priceCumulative = _get(pair, blockTimestamp); uint144 priceAverage = FixedPoint.uq112x112(uint224((priceCumulative - pairs[pair].priceCumulativeLast) / timeElapsed)).mul(1e18).decode144(); return (true, priceAverage); } // Check the current spot exchange rate without any state changes /// @inheritdoc IOracle function peekSpot(bytes calldata data) external view override returns (uint256 rate) { IUniswapV2Pair pair = abi.decode(data, (IUniswapV2Pair)); (uint256 reserve0, uint256 reserve1, ) = pair.getReserves(); rate = reserve1.mul(1e18) / reserve0; } /// @inheritdoc IOracle function name(bytes calldata) public view override returns (string memory) { return "SushiSwap TWAP"; } /// @inheritdoc IOracle function symbol(bytes calldata) public view override returns (string memory) { return "S"; } }