// File: contracts/DerivativeOracles/PendleTokenCustomOracle.sol



// SPDX-License-Identifier: -- WISE --

import "./CustomOracleSetup.sol";

import "./../InterfaceHub/IERC20.sol";
import "./../WiseOracleHub/Libraries/OracleLibrary.sol";
import "./../WiseOracleHub/Libraries/IUniswapV3Factory.sol";

pragma solidity =0.8.23;

error InvalidPool();
error InvalidTokens();
error NoWethDetected();

contract PendleTokenCustomOracle is CustomOracleSetup {

    IUniswapV3Factory public immutable UNI_V3_FACTORY;

    uint256 public immutable globalAmountIn;

    address public immutable uniPool;
    address public immutable targetToken;

    address public immutable token0;
    address public immutable token1;

    uint8 immutable DECIMALS_PRECISION = 18;

    constructor(
        address _pendleToken,
        address _uniPool,
        address _token0,
        address _token1,
        address _weth,
        address _uniFactory,
        uint24 _fee,
        uint8 _decimals
    )
        CustomOracleSetup()
    {
        targetToken = _pendleToken;
        DECIMALS_PRECISION = _decimals;
        uniPool = _uniPool;
        globalAmountIn = 10 ** IERC20(targetToken).decimals();
        token0 = _token0;
        token1 = _token1;

        if (_token1 != _pendleToken && _token0 != _pendleToken) {
            revert InvalidTokens();
        }

        if (_token1 != _weth && _token0 != _weth) {
            revert NoWethDetected();
        }

        UNI_V3_FACTORY = IUniswapV3Factory(
            _uniFactory
        );

        address pool = _getPool(
            _token0,
            _token1,
            _fee
        );

        if (pool != _uniPool) {
            revert InvalidPool();
        }
    }

    /**
    * @dev Retrieves the pool address for given
    * tokens and fee from Uniswap V3 Factory.
    */
    function _getPool(
        address _token0,
        address _token1,
        uint24 _fee
    )
        internal
        view
        returns (address pool)
    {
        return UNI_V3_FACTORY.getPool(
            _token0,
            _token1,
            _fee
        );
    }

    function latestAnswer()
        public
        view
        returns (uint256 amountOutForOneTokenIn)
    {
        (
            ,
            int24 tick
            ,
            ,
            ,
            ,
            ,
        ) = IUniswapV3Pool(uniPool).slot0();

        address tokenIn = targetToken;
        uint128 amountIn = uint128(
            globalAmountIn
        );

        address tokenOut = targetToken == token1
            ? token0
            : token1;

        amountOutForOneTokenIn = OracleLibrary.getQuoteAtTick(
            tick,
            amountIn,
            tokenIn,
            tokenOut
        );
    }

    function decimals()
        external
        pure
        returns (uint8)
    {
        return DECIMALS_PRECISION;
    }

    function latestRoundData()
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answerdInRound
        )
    {
        roundId = globalRoundId;
        updatedAt = lastUpdateGlobal;

        return (
            roundId,
            int256(latestAnswer()),
            startedAt,
            updatedAt,
            answerdInRound
        );
    }

    function getRoundData(
        uint80 _roundId
    )
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        )
    {
        updatedAt = timeStampByRoundId[
            _roundId
        ];

        return (
            _roundId,
            int256(latestAnswer()),
            startedAt,
            updatedAt,
            answeredInRound
        );
    }
}

// File: contracts/WiseOracleHub/Libraries/IUniswapV3Factory.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

/// @title The interface for the Uniswap V3 Factory
/// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees
interface IUniswapV3Factory {

    /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist
    /// @dev _tokenA and tokenB may be passed in either token0/token1 or token1/token0 order
    /// @param _tokenA The contract address of either token0 or token1
    /// @param _tokenB The contract address of the other token
    /// @param _fee The fee collected upon every swap in the pool, denominated in hundredths of a bip
    /// @return pool The pool address
    function getPool(
        address _tokenA,
        address _tokenB,
        uint24 _fee
    )
        external
        view
        returns (address pool);
}

// File: contracts/WiseOracleHub/Libraries/OracleLibrary.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

import "./FullMath.sol";
import "./TickMath.sol";
import "./IUniswapV3Pool.sol";

/// @title Oracle library
/// @notice Provides functions to integrate with V3 pool oracle
library OracleLibrary {

    /// @notice Given a tick and a token amount, calculates the amount of token received in exchange
    /// @param _tick Tick value used to calculate the quote
    /// @param _baseAmount Amount of token to be converted
    /// @param _baseToken Address of an ERC20 token contract used as the baseAmount denomination
    /// @param _quoteToken Address of an ERC20 token contract used as the quoteAmount denomination
    /// @return quoteAmount Amount of quoteToken received for baseAmount of baseToken
    function getQuoteAtTick(
        int24 _tick,
        uint128 _baseAmount,
        address _baseToken,
        address _quoteToken
    )
        internal
        pure
        returns (uint256 quoteAmount)
    {
        uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(
            _tick
        );

        // Calculate quoteAmount with better precision
        // if it doesn't overflow when multiplied by itself

        if (sqrtRatioX96 <= type(uint128).max) {

            uint256 ratioX192 = uint256(sqrtRatioX96)
                * sqrtRatioX96;

            quoteAmount = _baseToken < _quoteToken
                ? FullMath.mulDiv(
                    ratioX192,
                    _baseAmount,
                    1 << 192
                )
                : FullMath.mulDiv(
                    1 << 192,
                    _baseAmount,
                    ratioX192
                );

            return quoteAmount;
        }

        uint256 ratioX128 = FullMath.mulDiv(
            sqrtRatioX96,
            sqrtRatioX96,
            1 << 64
        );

        quoteAmount = _baseToken < _quoteToken
            ? FullMath.mulDiv(
                ratioX128,
                _baseAmount,
                1 << 128
            )
            : FullMath.mulDiv(
                1 << 128,
                _baseAmount,
                ratioX128
            );

        return quoteAmount;
    }
}

// File: contracts/InterfaceHub/IERC20.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

interface IERC20 {

    function totalSupply()
        external
        view
        returns (uint256);

    function balanceOf(
        address _account
    )
        external
        view
        returns (uint256);

    function transferFrom(
        address _sender,
        address _recipient,
        uint256 _amount
    )
        external
        returns (bool);

    function transfer(
        address _recipient,
        uint256 _amount
    )
        external
        returns (bool);

    function allowance(
        address owner,
        address spender
    )
        external
        view
        returns (uint256);

    function approve(
        address _spender,
        uint256 _amount
    )
        external
        returns (bool);

    function decimals()
        external
        view
        returns (uint8);

    event Transfer(
        address indexed from,
        address indexed to,
        uint256 value
    );

    event Approval(
        address indexed owner,
        address indexed spender,
        uint256 value
    );

    event  Deposit(
        address indexed dst,
        uint wad
    );

    event  Withdrawal(
        address indexed src,
        uint wad
    );
}

// File: contracts/DerivativeOracles/CustomOracleSetup.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

contract CustomOracleSetup {

    address public master;
    uint256 public lastUpdateGlobal;

    uint80 public globalRoundId;

    mapping(uint80 => uint256) public timeStampByRoundId;

    modifier onlyOwner() {
        require(
            msg.sender == master,
            "CustomOracleSetup: NOT_MASTER"
        );
        _;
    }

    constructor() {
        master = msg.sender;
    }

    function renounceOwnership()
        external
        onlyOwner
    {
        master = address(0x0);
    }

    function setLastUpdateGlobal(
        uint256 _time
    )
        external
        onlyOwner
    {
        lastUpdateGlobal = _time;
    }

    function setRoundData(
        uint80 _roundId,
        uint256 _updateTime
    )
        external
        onlyOwner
    {
        timeStampByRoundId[_roundId] = _updateTime;
    }

    function setGlobalAggregatorRoundId(
        uint80 _aggregatorRoundId
    )
        external
        onlyOwner
    {
        globalRoundId = _aggregatorRoundId;
    }

    function getTimeStamp()
        external
        view
        returns (uint256)
    {
        return block.timestamp;
    }
}

// File: contracts/WiseOracleHub/Libraries/IUniswapV3Pool.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

import './IUniswapV3PoolDerivedState.sol';

/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is IUniswapV3PoolDerivedState {
    function slot0(
  )
    external
    view
    returns (
        uint160 sqrtPriceX96,
        int24 tick,
        uint16 observationIndex,
        uint16 observationCardinality,
        uint16 observationCardinalityNext,
        uint8 feeProtocol,
        bool unlocked
    );
}

// File: contracts/WiseOracleHub/Libraries/TickMath.sol



// SPDX-License-Identifier: GPL-2.0-or-later

pragma solidity =0.8.23;

/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {

    /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
    int24 internal constant MIN_TICK = -887272;

    /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
    int24 internal constant MAX_TICK = -MIN_TICK;

    /// @notice Calculates sqrt(1.0001^tick) * 2^96
    /// @dev Throws if |tick| > max tick
    /// @param _tick The input tick for the above formula
    /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
    /// at the given tick
    function getSqrtRatioAtTick(
        int24 _tick
    )
        internal
        pure
        returns (uint160 sqrtPriceX96)
    {
        uint256 absTick = _tick < 0
            ? uint256(-int256(_tick))
            : uint256(int256(_tick));

        require(
            absTick <= uint256(uint24(MAX_TICK)),
            "TickMath: TICK_OVERFLOW"
        );

        uint256 ratio = absTick & 0x1 != 0
            ? 0xfffcb933bd6fad37aa2d162d1a594001
            : 0x100000000000000000000000000000000;

        if (absTick & 0x2 != 0) ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
        if (absTick & 0x4 != 0) ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
        if (absTick & 0x8 != 0) ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
        if (absTick & 0x10 != 0) ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
        if (absTick & 0x20 != 0) ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
        if (absTick & 0x40 != 0) ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
        if (absTick & 0x80 != 0) ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
        if (absTick & 0x100 != 0) ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
        if (absTick & 0x200 != 0) ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
        if (absTick & 0x400 != 0) ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
        if (absTick & 0x800 != 0) ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
        if (absTick & 0x1000 != 0) ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
        if (absTick & 0x2000 != 0) ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
        if (absTick & 0x4000 != 0) ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
        if (absTick & 0x8000 != 0) ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
        if (absTick & 0x10000 != 0) ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
        if (absTick & 0x20000 != 0) ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
        if (absTick & 0x40000 != 0) ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
        if (absTick & 0x80000 != 0) ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;

        if (_tick > 0) {
            ratio = type(uint256).max / ratio;
        }

        // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
        // we then downcast because we know the result always fits within 160 bits due to our tick input constraint
        // we round up in the division so getTickAtSqrtRatio of the output price is always consistent
        sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
    }
}

// File: contracts/WiseOracleHub/Libraries/FullMath.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
    /// @param _a The multiplicand
    /// @param _b The multiplier
    /// @param _denominator The divisor
    /// @return result The 256-bit result
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
    function mulDiv(
        uint256 _a,
        uint256 _b,
        uint256 _denominator
    )
        internal
        pure
        returns (uint256 result)
    {
        // 512-bit multiply [prod1 prod0] = a * b
        // Compute the product mod 2**256 and mod 2**256 - 1
        // then use the Chinese Remainder Theorem to reconstruct
        // the 512 bit result. The result is stored in two 256
        // variables such that product = prod1 * 2**256 + prod0
        uint256 prod0; // Least significant 256 bits of the product
        uint256 prod1; // Most significant 256 bits of the product
        assembly {
            let mm := mulmod(_a, _b, not(0))
            prod0 := mul(_a, _b)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        // Handle non-overflow cases, 256 by 256 division
        if (prod1 == 0) {
            require(_denominator > 0);
            assembly {
                result := div(prod0, _denominator)
            }
            return result;
        }

        // Make sure the result is less than 2**256.
        // Also prevents _denominator == 0
        require(_denominator > prod1);

        ///////////////////////////////////////////////
        // 512 by 256 division.
        ///////////////////////////////////////////////

        // Make division exact by subtracting the remainder from [prod1 prod0]
        // Compute remainder using mulmod
        uint256 remainder;
        assembly {
            remainder := mulmod(_a, _b, _denominator)
        }
        // Subtract 256 bit number from 512 bit number
        assembly {
            prod1 := sub(prod1, gt(remainder, prod0))
            prod0 := sub(prod0, remainder)
        }

        // Factor powers of two out of _denominator
        // Compute largest power of two divisor of _denominator.
        // Always >= 1.
        uint256 twos;
        assembly {
            // Perform the NOT operation, equivalent to bitwise negation
            let negated := not(_denominator)

            // Add 1 to get the two's complement (equivalent to negation in this context)
            negated := add(negated, 1)

            // Perform the AND operation with the original _denominator
            twos := and(negated, _denominator)
        }
        // Divide _denominator by power of two
        assembly {
            _denominator := div(_denominator, twos)
        }

        // Divide [prod1 prod0] by the factors of two
        assembly {
            prod0 := div(prod0, twos)
        }
        // Shift in bits from prod1 into prod0. For this we need
        // to flip `twos` such that it is 2**256 / twos.
        // If twos is zero, then it becomes one
        assembly {
            twos := add(div(sub(0, twos), twos), 1)
        }
        prod0 |= prod1 * twos;

        // Invert _denominator mod 2**256
        // Now that _denominator is an odd number, it has an inverse
        // modulo 2**256 such that _denominator * inv = 1 mod 2**256.
        // Compute the inverse by starting with a seed that is correct
        // correct for four bits. That is, _denominator * inv = 1 mod 2**4
        uint256 inv = (3 * _denominator) ^ 2;
        // Now use Newton-Raphson iteration to improve the precision.
        // Thanks to Hensel's lifting lemma, this also works in modular
        // arithmetic, doubling the correct bits in each step.
        inv *= 2 - _denominator * inv; // inverse mod 2**8
        inv *= 2 - _denominator * inv; // inverse mod 2**16
        inv *= 2 - _denominator * inv; // inverse mod 2**32
        inv *= 2 - _denominator * inv; // inverse mod 2**64
        inv *= 2 - _denominator * inv; // inverse mod 2**128
        inv *= 2 - _denominator * inv; // inverse mod 2**256

        // Because the division is now exact we can divide by multiplying
        // with the modular inverse of _denominator. This will give us the
        // correct result modulo 2**256. Since the precoditions guarantee
        // that the outcome is less than 2**256, this is the final result.
        // We don't need to compute the high bits of the result and prod1
        // is no longer required.
        result = prod0 * inv;
        return result;
    }
}

// File: contracts/WiseOracleHub/Libraries/IUniswapV3PoolDerivedState.sol



// SPDX-License-Identifier: -- WISE --

pragma solidity =0.8.23;

interface IUniswapV3PoolDerivedState {

    /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp
    /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing
    /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,
    /// you must call it with secondsAgos = [3600, 0].
    /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
    /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
    /// @param _secondsAgos From how long ago each cumulative tick and liquidity value should be returned
    /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
    /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block
    /// timestamp
    function observe(
        uint32[] calldata _secondsAgos
    )
        external
        view
        returns (
            int56[] memory tickCumulatives,
            uint160[] memory secondsPerLiquidityCumulativeX128s
        );
}