contract NewOracleProxy is Ownable {
        using SafeMath for uint256;

    string public oracleName;
    uint8  public oracleDecimal;

    address public chainlink;
    address public uniswapPair;
    uint256 public chainlinkThreshold;
    bool    public uniswapPairReverse;

    uint256 public T0 = 900;
    uint256 public RATIO = 2;

    uint256 public uniswapPriceCumulativeLast;
    uint256 public uniswapPriceLast;
    uint32  public uniswapBlockTimestampLast;

    event DevivationTooBig (uint256 _chainlinPrice, uint256 _dexPrice);

    constructor(address _chainlink,
                address _uniswapPair,
                string memory _name, 
                uint256 _chainlinkThreshold,
                bool _uniswapPairReverse) public {
        chainlink = _chainlink;
        uniswapPair = _uniswapPair;
        oracleName = _name;
        oracleDecimal =  18;
        chainlinkThreshold = _chainlinkThreshold;
        uniswapPairReverse = _uniswapPairReverse;

        (uniswapPriceCumulativeLast, uniswapPriceLast, uniswapBlockTimestampLast)
                = _getUniswapPrice(uniswapPair, uniswapPairReverse);
    }

    // administration
    function setT0(uint256 t0) external onlyOwner {
        T0 = t0;
    }

    function setRATIO(uint256 ratio) external onlyOwner {
        RATIO = ratio;
    }

    // oracle
    function getChainlinkPrice() external view returns (uint256, uint256) {
        (, int256 answer, , uint256 updateTimestamp, ) 
            = AggregatorV3Interface(chainlink).latestRoundData();
        uint8 chainlinkDecimal = AggregatorV3Interface(chainlink).decimals(); 
        uint256 price = uint256(answer);

        if (chainlinkDecimal > oracleDecimal) {
            price = price.div(10 ** uint256(chainlinkDecimal - oracleDecimal));
        } else if (chainlinkDecimal < oracleDecimal) {
            price = price.mul(10 ** uint256(oracleDecimal - chainlinkDecimal));
        } 

        return (price, updateTimestamp);
    }

    // return price with decimal 18
    function getUniswapPrice(address pair, bool reverse) external view
        returns (uint256, uint32) {
        (uint256 priceCumulative,  , uint32 blockTimestamp) = _getUniswapPrice(pair, reverse);
        return (priceCumulative, blockTimestamp);
    }

    // return price with decimal 18
    function _getUniswapPrice(address pair, bool reverse) internal view
        returns (uint256, uint256, uint32) {
        // unit = (2 ** 112) / (1e18)
        uint256 unit = 5192296858534827;

        uint256 price0 = IUniswapV2Pair(pair).price0CumulativeLast();
        uint256 price1 = IUniswapV2Pair(pair).price1CumulativeLast();
        (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast) = IUniswapV2Pair(pair).getReserves(); 

        // decimal 18
        uint256 priceLast = 10 ** 18;

        // check pair token are in the same order?
        if (reverse == false) {
            priceLast = priceLast.mul(reserve1).div(reserve0);
            return (price0.div(unit).div(10**12), priceLast.div(10**12), uint32(blockTimestampLast % 2 ** 32));
        } else {
            priceLast = priceLast.mul(reserve0).div(reserve1);
            return (price1.div(unit).mul(10**12), priceLast.mul(10**12), uint32(blockTimestampLast % 2 ** 32));
        }
    }

    function getPrice() external returns (uint256, uint8) {

        uint256 uniswapPriceCumulative;
        uint256 uniswapPrice;
        uint32  uniswapBlockTimestamp;

        uint256 targetPrice;
        uint256 chainlinkPrice;
        uint256 chainlinkTimestamp;
        uint256 dexPrice;

        uint256 deltaT;
        uint256 deviation;
        uint8   deviationLevel;

        (chainlinkPrice, chainlinkTimestamp) = this.getChainlinkPrice();

        (uniswapPriceCumulative, ,uniswapBlockTimestamp) 
                        = _getUniswapPrice(uniswapPair, uniswapPairReverse);

        if (uniswapBlockTimestamp != uniswapBlockTimestampLast) {
            uniswapPrice = uniswapPriceCumulative.sub(uniswapPriceCumulativeLast).
                div(uniswapBlockTimestamp-uniswapBlockTimestampLast);
        } else {
            uniswapPrice = uniswapPriceLast;
        }
        dexPrice = uniswapPrice;

        // dexPrice is newer than oraclePrice
        if (uint256(uniswapBlockTimestampLast) >= chainlinkTimestamp) {
            // detaT = (uniswapBlockTimestamp + uniswapBlockTimestampLast)/2 - oracleTimestamp
            deltaT = uint256((uniswapBlockTimestamp + uniswapBlockTimestampLast)/2).sub(chainlinkTimestamp);
        } else {
            deltaT = 0;
            dexPrice = chainlinkPrice;
        }

        // last step
        deviationLevel = 0;
        if (dexPrice > chainlinkPrice) {
            deviation = dexPrice.sub(chainlinkPrice).mul(10 ** 18).div(chainlinkPrice);
            if (deviation > chainlinkThreshold.mul(RATIO)) {
                targetPrice = chainlinkPrice;
                // set to biggest
                //deviationLevel = uint8(deviation.div(chainlinkThreshold));
                deviationLevel = 255;
                emit DevivationTooBig(chainlinkPrice, dexPrice);
            } else {
                // targetPrice = (chainlinkPrice * T0 + dexPrice * deltaT) / (T0 + deltaT)
                targetPrice = chainlinkPrice.mul(T0).add(dexPrice.mul(deltaT)).div(deltaT.add(T0)); 
            }
        } else if (dexPrice < chainlinkPrice) {
            deviation = chainlinkPrice.sub(dexPrice).mul(10 ** 18).div(dexPrice);
            if (deviation > chainlinkThreshold.mul(RATIO)) {
                targetPrice = chainlinkPrice;
                // set to biggest
                //deviationLevel = uint8(deviation/chainlinkThreshold);
                deviationLevel = 255;
                emit DevivationTooBig(chainlinkPrice, dexPrice);
            } else {
                // targetPrice = (chainlinkPrice * T0 + dexPrice * deltaT) / (T0 + deltaT)
                targetPrice = chainlinkPrice.mul(T0).add(dexPrice.mul(deltaT)).div(deltaT.add(T0)); 
            }
        } else {
            // dexPrice == chainlinkPrice
            targetPrice = chainlinkPrice;
        }

        if (uniswapBlockTimestamp != uniswapBlockTimestampLast) {
            uniswapPriceCumulativeLast = uniswapPriceCumulative;
            uniswapPriceLast = uniswapPrice;
            uniswapBlockTimestampLast = uniswapBlockTimestamp;
        }

        return (targetPrice, deviationLevel);
    }
}
// NewOracleProxy.sol
function getChainlinkPrice() external view returns (uint256, uint256) {
    (, int256 answer, , uint256 updateTimestamp, ) 
        = AggregatorV3Interface(chainlink).latestRoundData();
    uint8 chainlinkDecimal = AggregatorV3Interface(chainlink).decimals(); 
    uint256 price = uint256(answer);

    if (chainlinkDecimal > oracleDecimal) {
        price = price.div(10 ** uint256(chainlinkDecimal - oracleDecimal));
    } else if (chainlinkDecimal < oracleDecimal) {
        price = price.mul(10 ** uint256(oracleDecimal - chainlinkDecimal));
    } 

    return (price, updateTimestamp);
}

// oracle