ETH Price: $2,514.37 (-0.33%)

Transaction Decoder

Block:
20834160 at Sep-26-2024 10:22:11 AM +UTC
Transaction Fee:
0.000825653705752245 ETH $2.08
Gas Used:
61,865 Gas / 13.346055213 Gwei

Account State Difference:

  Address   Before After State Difference Code
(beaverbuild)
15.739899970109888259 Eth15.739972044883238409 Eth0.00007207477335015
0x9A559e93...9fb1112F7
0xc2EB0534...d524B3488
0.004302573676812611 Eth
Nonce: 3925
0.003476919971060366 Eth
Nonce: 3926
0.000825653705752245

Execution Trace

UniswapV2OracleV2.CALL( )
  • UniswapV2Pair.STATICCALL( )
  • UniswapV2Pair.STATICCALL( )
  • UniswapV2Pair.STATICCALL( )
    File 1 of 2: UniswapV2OracleV2
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol)
    pragma solidity ^0.8.0;
    import "../utils/Context.sol";
    /**
     * @dev Contract module which provides a basic access control mechanism, where
     * there is an account (an owner) that can be granted exclusive access to
     * specific functions.
     *
     * By default, the owner account will be the one that deploys the contract. This
     * can later be changed with {transferOwnership}.
     *
     * This module is used through inheritance. It will make available the modifier
     * `onlyOwner`, which can be applied to your functions to restrict their use to
     * the owner.
     */
    abstract contract Ownable is Context {
        address private _owner;
        event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
        /**
         * @dev Initializes the contract setting the deployer as the initial owner.
         */
        constructor() {
            _transferOwnership(_msgSender());
        }
        /**
         * @dev Throws if called by any account other than the owner.
         */
        modifier onlyOwner() {
            _checkOwner();
            _;
        }
        /**
         * @dev Returns the address of the current owner.
         */
        function owner() public view virtual returns (address) {
            return _owner;
        }
        /**
         * @dev Throws if the sender is not the owner.
         */
        function _checkOwner() internal view virtual {
            require(owner() == _msgSender(), "Ownable: caller is not the owner");
        }
        /**
         * @dev Leaves the contract without owner. It will not be possible to call
         * `onlyOwner` functions. Can only be called by the current owner.
         *
         * NOTE: Renouncing ownership will leave the contract without an owner,
         * thereby disabling any functionality that is only available to the owner.
         */
        function renounceOwnership() public virtual onlyOwner {
            _transferOwnership(address(0));
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Can only be called by the current owner.
         */
        function transferOwnership(address newOwner) public virtual onlyOwner {
            require(newOwner != address(0), "Ownable: new owner is the zero address");
            _transferOwnership(newOwner);
        }
        /**
         * @dev Transfers ownership of the contract to a new account (`newOwner`).
         * Internal function without access restriction.
         */
        function _transferOwnership(address newOwner) internal virtual {
            address oldOwner = _owner;
            _owner = newOwner;
            emit OwnershipTransferred(oldOwner, newOwner);
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts v4.4.1 (utils/Context.sol)
    pragma solidity ^0.8.0;
    /**
     * @dev Provides information about the current execution context, including the
     * sender of the transaction and its data. While these are generally available
     * via msg.sender and msg.data, they should not be accessed in such a direct
     * manner, since when dealing with meta-transactions the account sending and
     * paying for execution may not be the actual sender (as far as an application
     * is concerned).
     *
     * This contract is only required for intermediate, library-like contracts.
     */
    abstract contract Context {
        function _msgSender() internal view virtual returns (address) {
            return msg.sender;
        }
        function _msgData() internal view virtual returns (bytes calldata) {
            return msg.data;
        }
    }
    // SPDX-License-Identifier: MIT
    // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/SafeMath.sol)
    pragma solidity ^0.8.0;
    // CAUTION
    // This version of SafeMath should only be used with Solidity 0.8 or later,
    // because it relies on the compiler's built in overflow checks.
    /**
     * @dev Wrappers over Solidity's arithmetic operations.
     *
     * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
     * now has built in overflow checking.
     */
    library SafeMath {
        /**
         * @dev Returns the addition of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                uint256 c = a + b;
                if (c < a) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b > a) return (false, 0);
                return (true, a - b);
            }
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
         *
         * _Available since v3.4._
         */
        function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
                // benefit is lost if 'b' is also tested.
                // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
                if (a == 0) return (true, 0);
                uint256 c = a * b;
                if (c / a != b) return (false, 0);
                return (true, c);
            }
        }
        /**
         * @dev Returns the division of two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a / b);
            }
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
         *
         * _Available since v3.4._
         */
        function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
            unchecked {
                if (b == 0) return (false, 0);
                return (true, a % b);
            }
        }
        /**
         * @dev Returns the addition of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `+` operator.
         *
         * Requirements:
         *
         * - Addition cannot overflow.
         */
        function add(uint256 a, uint256 b) internal pure returns (uint256) {
            return a + b;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting on
         * overflow (when the result is negative).
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b) internal pure returns (uint256) {
            return a - b;
        }
        /**
         * @dev Returns the multiplication of two unsigned integers, reverting on
         * overflow.
         *
         * Counterpart to Solidity's `*` operator.
         *
         * Requirements:
         *
         * - Multiplication cannot overflow.
         */
        function mul(uint256 a, uint256 b) internal pure returns (uint256) {
            return a * b;
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator.
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b) internal pure returns (uint256) {
            return a / b;
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting when dividing by zero.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b) internal pure returns (uint256) {
            return a % b;
        }
        /**
         * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
         * overflow (when the result is negative).
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {trySub}.
         *
         * Counterpart to Solidity's `-` operator.
         *
         * Requirements:
         *
         * - Subtraction cannot overflow.
         */
        function sub(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            unchecked {
                require(b <= a, errorMessage);
                return a - b;
            }
        }
        /**
         * @dev Returns the integer division of two unsigned integers, reverting with custom message on
         * division by zero. The result is rounded towards zero.
         *
         * Counterpart to Solidity's `/` operator. Note: this function uses a
         * `revert` opcode (which leaves remaining gas untouched) while Solidity
         * uses an invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function div(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            unchecked {
                require(b > 0, errorMessage);
                return a / b;
            }
        }
        /**
         * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
         * reverting with custom message when dividing by zero.
         *
         * CAUTION: This function is deprecated because it requires allocating memory for the error
         * message unnecessarily. For custom revert reasons use {tryMod}.
         *
         * Counterpart to Solidity's `%` operator. This function uses a `revert`
         * opcode (which leaves remaining gas untouched) while Solidity uses an
         * invalid opcode to revert (consuming all remaining gas).
         *
         * Requirements:
         *
         * - The divisor cannot be zero.
         */
        function mod(uint256 a, uint256 b, string memory errorMessage) internal pure returns (uint256) {
            unchecked {
                require(b > 0, errorMessage);
                return a % b;
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    interface IOracle {
        function viewPriceInUSD() external view returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    import {IOracle} from './IOracle.sol';
    interface IPriceOracleAggregator {
        event UpdateOracle(address token, IOracle oracle);
        function updateOracleForAsset(address _asset, IOracle _oracle) external;
        function viewPriceInUSD(address _token) external view returns (uint256);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    interface IUniswapV2Factory {
        function getPair(
            address tokenA,
            address tokenB
        ) external view returns (address pair);
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    interface IUniswapV2Pair {
        event Approval(address indexed owner, address indexed spender, uint value);
        event Transfer(address indexed from, address indexed to, uint value);
        function name() external pure returns (string memory);
        function symbol() external pure returns (string memory);
        function decimals() external pure returns (uint8);
        function totalSupply() external view returns (uint);
        function balanceOf(address owner) external view returns (uint);
        function allowance(
            address owner,
            address spender
        ) external view returns (uint);
        function approve(address spender, uint value) external returns (bool);
        function transfer(address to, uint value) external returns (bool);
        function transferFrom(
            address from,
            address to,
            uint value
        ) external returns (bool);
        function DOMAIN_SEPARATOR() external view returns (bytes32);
        function PERMIT_TYPEHASH() external pure returns (bytes32);
        function nonces(address owner) external view returns (uint);
        function permit(
            address owner,
            address spender,
            uint value,
            uint deadline,
            uint8 v,
            bytes32 r,
            bytes32 s
        ) external;
        event Mint(address indexed sender, uint amount0, uint amount1);
        event Burn(
            address indexed sender,
            uint amount0,
            uint amount1,
            address indexed to
        );
        event Swap(
            address indexed sender,
            uint amount0In,
            uint amount1In,
            uint amount0Out,
            uint amount1Out,
            address indexed to
        );
        event Sync(uint112 reserve0, uint112 reserve1);
        function MINIMUM_LIQUIDITY() external pure returns (uint);
        function factory() external view returns (address);
        function token0() external view returns (address);
        function token1() external view returns (address);
        function getReserves()
            external
            view
            returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
        function price0CumulativeLast() external view returns (uint);
        function price1CumulativeLast() external view returns (uint);
        function kLast() external view returns (uint);
        function mint(address to) external returns (uint liquidity);
        function burn(address to) external returns (uint amount0, uint amount1);
        function swap(
            uint amount0Out,
            uint amount1Out,
            address to,
            bytes calldata data
        ) external;
        function skim(address to) external;
        function sync() external;
        function initialize(address, address) external;
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    library Babylonian {
        function sqrt(uint256 y) internal pure returns (uint256 z) {
            if (y > 3) {
                z = y;
                uint256 x = y / 2 + 1;
                while (x < z) {
                    z = x;
                    x = (y / x + x) / 2;
                }
            } else if (y != 0) {
                z = 1;
            }
            // else z = 0
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    import './Babylonian.sol';
    // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
    library FixedPoint {
        // range: [0, 2**112 - 1]
        // resolution: 1 / 2**112
        struct uq112x112 {
            uint224 _x;
        }
        // range: [0, 2**144 - 1]
        // resolution: 1 / 2**112
        struct uq144x112 {
            uint256 _x;
        }
        uint8 private constant RESOLUTION = 112;
        uint256 private constant Q112 = uint256(1) << RESOLUTION;
        uint256 private constant Q224 = Q112 << RESOLUTION;
        // encode a uint112 as a UQ112x112
        function encode(uint112 x) internal pure returns (uq112x112 memory) {
            return uq112x112(uint224(x) << RESOLUTION);
        }
        // encodes a uint144 as a UQ144x112
        function encode144(uint144 x) internal pure returns (uq144x112 memory) {
            return uq144x112(uint256(x) << RESOLUTION);
        }
        // divide a UQ112x112 by a uint112, returning a UQ112x112
        function div(
            uq112x112 memory self,
            uint112 x
        ) internal pure returns (uq112x112 memory) {
            require(x != 0, 'FixedPoint: DIV_BY_ZERO');
            return uq112x112(self._x / uint224(x));
        }
        // multiply a UQ112x112 by a uint, returning a UQ144x112
        // reverts on overflow
        function mul(
            uq112x112 memory self,
            uint256 y
        ) internal pure returns (uq144x112 memory) {
            uint256 z;
            require(
                y == 0 || (z = uint256(self._x) * y) / y == uint256(self._x),
                'FixedPoint: MULTIPLICATION_OVERFLOW'
            );
            return uq144x112(z);
        }
        // returns a UQ112x112 which represents the ratio of the numerator to the denominator
        // equivalent to encode(numerator).div(denominator)
        function fraction(
            uint112 numerator,
            uint112 denominator
        ) internal pure returns (uq112x112 memory) {
            require(denominator > 0, 'FixedPoint: DIV_BY_ZERO');
            return uq112x112((uint224(numerator) << RESOLUTION) / denominator);
        }
        // decode a UQ112x112 into a uint112 by truncating after the radix point
        function decode(uq112x112 memory self) internal pure returns (uint112) {
            return uint112(self._x >> RESOLUTION);
        }
        // decode a UQ144x112 into a uint144 by truncating after the radix point
        function decode144(uq144x112 memory self) internal pure returns (uint144) {
            return uint144(self._x >> RESOLUTION);
        }
        // take the reciprocal of a UQ112x112
        function reciprocal(
            uq112x112 memory self
        ) internal pure returns (uq112x112 memory) {
            require(self._x != 0, 'FixedPoint: ZERO_RECIPROCAL');
            return uq112x112(uint224(Q224 / self._x));
        }
        // square root of a UQ112x112
        function sqrt(
            uq112x112 memory self
        ) internal pure returns (uq112x112 memory) {
            return uq112x112(uint224(Babylonian.sqrt(uint256(self._x)) << 56));
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    import '@openzeppelin/contracts/utils/math/SafeMath.sol';
    import '@openzeppelin/contracts/access/Ownable.sol';
    import '../libraries/FixedPoint.sol';
    import '../interfaces/IPriceOracleAggregator.sol';
    import '../interfaces/IUniswapV2Pair.sol';
    import '../interfaces/IUniswapV2Factory.sol';
    interface IERC20Metadata {
        function decimals() external view returns (uint8);
    }
    library UniswapV2Library {
        using SafeMath for uint256;
        // returns sorted token addresses, used to handle return values from pairs sorted in this order
        function sortTokens(
            address tokenA,
            address tokenB
        ) internal pure returns (address token0, address token1) {
            require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
            (token0, token1) = tokenA < tokenB
                ? (tokenA, tokenB)
                : (tokenB, tokenA);
            require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
        }
        // Less efficient than the CREATE2 method below
        function pairFor(
            address factory,
            address tokenA,
            address tokenB
        ) internal view returns (address pair) {
            (address token0, address token1) = sortTokens(tokenA, tokenB);
            pair = IUniswapV2Factory(factory).getPair(token0, token1);
        }
        // calculates the CREATE2 address for a pair without making any external calls
        function pairForCreate2(
            address factory,
            address tokenA,
            address tokenB
        ) internal pure returns (address pair) {
            (address token0, address token1) = sortTokens(tokenA, tokenB);
            pair = address(
                uint160(
                    bytes20(
                        keccak256(
                            abi.encodePacked(
                                hex'ff',
                                factory,
                                keccak256(abi.encodePacked(token0, token1)),
                                hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
                            )
                        )
                    )
                )
            ); // this matches the CREATE2 in UniswapV2Factory.createPair
        }
        // fetches and sorts the reserves for a pair
        function getReserves(
            address factory,
            address tokenA,
            address tokenB
        ) internal view returns (uint256 reserveA, uint256 reserveB) {
            (address token0, ) = sortTokens(tokenA, tokenB);
            (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(
                pairFor(factory, tokenA, tokenB)
            ).getReserves();
            (reserveA, reserveB) = tokenA == token0
                ? (reserve0, reserve1)
                : (reserve1, reserve0);
        }
        // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
        function quote(
            uint256 amountA,
            uint256 reserveA,
            uint256 reserveB
        ) internal pure returns (uint256 amountB) {
            require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
            require(
                reserveA > 0 && reserveB > 0,
                'UniswapV2Library: INSUFFICIENT_LIQUIDITY'
            );
            amountB = amountA.mul(reserveB) / reserveA;
        }
        // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
        function getAmountOut(
            uint256 amountIn,
            uint256 reserveIn,
            uint256 reserveOut
        ) internal pure returns (uint256 amountOut) {
            require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
            require(
                reserveIn > 0 && reserveOut > 0,
                'UniswapV2Library: INSUFFICIENT_LIQUIDITY'
            );
            uint256 amountInWithFee = amountIn.mul(997);
            uint256 numerator = amountInWithFee.mul(reserveOut);
            uint256 denominator = reserveIn.mul(1000).add(amountInWithFee);
            amountOut = numerator / denominator;
        }
        // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
        function getAmountIn(
            uint256 amountOut,
            uint256 reserveIn,
            uint256 reserveOut
        ) internal pure returns (uint256 amountIn) {
            require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
            require(
                reserveIn > 0 && reserveOut > 0,
                'UniswapV2Library: INSUFFICIENT_LIQUIDITY'
            );
            uint256 numerator = reserveIn.mul(amountOut).mul(1000);
            uint256 denominator = reserveOut.sub(amountOut).mul(997);
            amountIn = (numerator / denominator).add(1);
        }
        // performs chained getAmountOut calculations on any number of pairs
        function getAmountsOut(
            address factory,
            uint256 amountIn,
            address[] memory path
        ) internal view returns (uint256[] memory amounts) {
            require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
            amounts = new uint256[](path.length);
            amounts[0] = amountIn;
            for (uint256 i = 0; i < path.length - 1; i++) {
                (uint256 reserveIn, uint256 reserveOut) = getReserves(
                    factory,
                    path[i],
                    path[i + 1]
                );
                amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
            }
        }
        // performs chained getAmountIn calculations on any number of pairs
        function getAmountsIn(
            address factory,
            uint256 amountOut,
            address[] memory path
        ) internal view returns (uint256[] memory amounts) {
            require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
            amounts = new uint256[](path.length);
            amounts[amounts.length - 1] = amountOut;
            for (uint256 i = path.length - 1; i > 0; i--) {
                (uint256 reserveIn, uint256 reserveOut) = getReserves(
                    factory,
                    path[i - 1],
                    path[i]
                );
                amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
            }
        }
    }
    library UniswapV2OracleLibrary {
        using FixedPoint for *;
        // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1]
        function currentBlockTimestamp() internal view returns (uint32) {
            return uint32(block.timestamp % 2 ** 32);
        }
        // produces the cumulative price using counterfactuals to save gas and avoid a call to sync.
        function currentCumulativePrices(
            address pair
        )
            internal
            view
            returns (
                uint256 price0Cumulative,
                uint256 price1Cumulative,
                uint32 blockTimestamp
            )
        {
            blockTimestamp = currentBlockTimestamp();
            price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();
            price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast();
            // 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();
            if (blockTimestampLast != blockTimestamp) {
                // subtraction overflow is desired
                uint32 timeElapsed = blockTimestamp - blockTimestampLast;
                // addition overflow is desired
                // counterfactual
                price0Cumulative +=
                    uint256(FixedPoint.fraction(reserve1, reserve0)._x) *
                    timeElapsed;
                // counterfactual
                price1Cumulative +=
                    uint256(FixedPoint.fraction(reserve0, reserve1)._x) *
                    timeElapsed;
            }
        }
    }
    contract UniswapV2Oracle is IOracle, Ownable {
        using FixedPoint for *;
        /// @notice oracle that returns price in USD
        IPriceOracleAggregator public immutable aggregator;
        uint256 public PERIOD = 1; // 1 hour TWAP (time-weighted average price)
        uint256 public CONSULT_LENIENCY = 120; // Used for being able to consult past the period end
        bool public ALLOW_STALE_CONSULTS = false; // If false, consult() will fail if the TWAP is stale
        IUniswapV2Pair public immutable pair;
        bool public isFirstToken;
        address public immutable token0;
        address public immutable token1;
        uint256 public price0CumulativeLast;
        uint256 public price1CumulativeLast;
        uint32 public blockTimestampLast;
        FixedPoint.uq112x112 public price0Average;
        FixedPoint.uq112x112 public price1Average;
        constructor(
            address _factory,
            address _tokenA,
            address _tokenB,
            address _priceOracleAggregator
        ) {
            require(
                _priceOracleAggregator != address(0),
                'UNIV2: Invalid Aggregator'
            );
            require(_factory != address(0), 'UNIV2: Invalid factory');
            require(_tokenA != address(0), 'UNIV2: Invalid tokenA');
            require(_tokenB != address(0), 'UNIV2: Invalid tokenB');
            aggregator = IPriceOracleAggregator(_priceOracleAggregator);
            IUniswapV2Pair _pair = IUniswapV2Pair(
                UniswapV2Library.pairFor(_factory, _tokenA, _tokenB)
            );
            require(address(_pair) != address(0), 'UNIV2: Invalid Pair');
            pair = _pair;
            token0 = _pair.token0();
            token1 = _pair.token1();
            price0CumulativeLast = _pair.price0CumulativeLast(); // fetch the current accumulated price value (1 / 0)
            price1CumulativeLast = _pair.price1CumulativeLast(); // fetch the current accumulated price value (0 / 1)
            uint112 reserve0;
            uint112 reserve1;
            (reserve0, reserve1, blockTimestampLast) = _pair.getReserves();
            require(reserve0 != 0 && reserve1 != 0, 'UNIV2: NO_RESERVES'); // ensure that there's liquidity in the pair
            if (_tokenA == _pair.token0()) {
                isFirstToken = true;
            } else {
                isFirstToken = false;
            }
        }
        function setPeriod(uint256 _period) external onlyOwner {
            PERIOD = _period;
        }
        function setConsultLeniency(uint256 _consult_leniency) external onlyOwner {
            CONSULT_LENIENCY = _consult_leniency;
        }
        function setAllowStaleConsults(
            bool _allow_stale_consults
        ) external onlyOwner {
            ALLOW_STALE_CONSULTS = _allow_stale_consults;
        }
        // Check if update() can be called instead of wasting gas calling it
        function canUpdate() public view returns (bool) {
            uint32 blockTimestamp = UniswapV2OracleLibrary.currentBlockTimestamp();
            uint32 timeElapsed = blockTimestamp - blockTimestampLast; // Overflow is desired
            return (timeElapsed >= PERIOD);
        }
        function update() external {
            (
                uint256 price0Cumulative,
                uint256 price1Cumulative,
                uint32 blockTimestamp
            ) = UniswapV2OracleLibrary.currentCumulativePrices(address(pair));
            // overflow is desired
            uint256 timeElapsed = blockTimestamp > blockTimestampLast
                ? blockTimestamp - blockTimestampLast
                : uint256(blockTimestamp) + 2 ** 32 - uint256(blockTimestampLast);
            // Ensure that at least one full period has passed since the last update
            require(timeElapsed >= PERIOD, 'UniswapPairOracle: PERIOD_NOT_ELAPSED');
            // Overflow is desired, casting never truncates
            // 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)
            );
            price1Average = FixedPoint.uq112x112(
                uint224((price1Cumulative - price1CumulativeLast) / timeElapsed)
            );
            price0CumulativeLast = price0Cumulative;
            price1CumulativeLast = price1Cumulative;
            blockTimestampLast = blockTimestamp;
        }
        /// @dev returns the latest price of asset
        function viewPriceInUSD() external view override returns (uint256 price) {
            uint32 blockTimestamp = UniswapV2OracleLibrary.currentBlockTimestamp();
            uint32 timeElapsed = blockTimestamp - blockTimestampLast; // Overflow is desired
            // Ensure that the price is not stale
            require(
                (timeElapsed < (PERIOD + CONSULT_LENIENCY)) || ALLOW_STALE_CONSULTS,
                'UniswapPairOracle: PRICE_IS_STALE_NEED_TO_CALL_UPDATE'
            );
            if (isFirstToken) {
                price =
                    (aggregator.viewPriceInUSD(token1) *
                        (10 ** IERC20Metadata(token0).decimals())) /
                    (
                        price1Average
                            .mul(10 ** IERC20Metadata(token1).decimals())
                            .decode144()
                    );
            } else {
                price =
                    (aggregator.viewPriceInUSD(token0) *
                        (10 ** IERC20Metadata(token1).decimals())) /
                    (
                        price0Average
                            .mul(10 ** IERC20Metadata(token0).decimals())
                            .decode144()
                    );
            }
        }
    }
    // SPDX-License-Identifier: MIT
    pragma solidity 0.8.17;
    import '@openzeppelin/contracts/utils/math/SafeMath.sol';
    import '@openzeppelin/contracts/access/Ownable.sol';
    import '../libraries/FixedPoint.sol';
    import '../interfaces/IPriceOracleAggregator.sol';
    import '../interfaces/IUniswapV2Pair.sol';
    import '../interfaces/IUniswapV2Factory.sol';
    import {UniswapV2Oracle} from './UniswapV2Oracle.sol';
    interface IERC20Metadata {
        function decimals() external view returns (uint8);
    }
    library UniswapV2Library {
        using SafeMath for uint256;
        // returns sorted token addresses, used to handle return values from pairs sorted in this order
        function sortTokens(
            address tokenA,
            address tokenB
        ) internal pure returns (address token0, address token1) {
            require(tokenA != tokenB, 'UniswapV2Library: IDENTICAL_ADDRESSES');
            (token0, token1) = tokenA < tokenB
                ? (tokenA, tokenB)
                : (tokenB, tokenA);
            require(token0 != address(0), 'UniswapV2Library: ZERO_ADDRESS');
        }
        // Less efficient than the CREATE2 method below
        function pairFor(
            address factory,
            address tokenA,
            address tokenB
        ) internal view returns (address pair) {
            (address token0, address token1) = sortTokens(tokenA, tokenB);
            pair = IUniswapV2Factory(factory).getPair(token0, token1);
        }
        // calculates the CREATE2 address for a pair without making any external calls
        function pairForCreate2(
            address factory,
            address tokenA,
            address tokenB
        ) internal pure returns (address pair) {
            (address token0, address token1) = sortTokens(tokenA, tokenB);
            pair = address(
                uint160(
                    bytes20(
                        keccak256(
                            abi.encodePacked(
                                hex'ff',
                                factory,
                                keccak256(abi.encodePacked(token0, token1)),
                                hex'96e8ac4277198ff8b6f785478aa9a39f403cb768dd02cbee326c3e7da348845f' // init code hash
                            )
                        )
                    )
                )
            ); // this matches the CREATE2 in UniswapV2Factory.createPair
        }
        // fetches and sorts the reserves for a pair
        function getReserves(
            address factory,
            address tokenA,
            address tokenB
        ) internal view returns (uint256 reserveA, uint256 reserveB) {
            (address token0, ) = sortTokens(tokenA, tokenB);
            (uint256 reserve0, uint256 reserve1, ) = IUniswapV2Pair(
                pairFor(factory, tokenA, tokenB)
            ).getReserves();
            (reserveA, reserveB) = tokenA == token0
                ? (reserve0, reserve1)
                : (reserve1, reserve0);
        }
        // given some amount of an asset and pair reserves, returns an equivalent amount of the other asset
        function quote(
            uint256 amountA,
            uint256 reserveA,
            uint256 reserveB
        ) internal pure returns (uint256 amountB) {
            require(amountA > 0, 'UniswapV2Library: INSUFFICIENT_AMOUNT');
            require(
                reserveA > 0 && reserveB > 0,
                'UniswapV2Library: INSUFFICIENT_LIQUIDITY'
            );
            amountB = amountA.mul(reserveB) / reserveA;
        }
        // given an input amount of an asset and pair reserves, returns the maximum output amount of the other asset
        function getAmountOut(
            uint256 amountIn,
            uint256 reserveIn,
            uint256 reserveOut
        ) internal pure returns (uint256 amountOut) {
            require(amountIn > 0, 'UniswapV2Library: INSUFFICIENT_INPUT_AMOUNT');
            require(
                reserveIn > 0 && reserveOut > 0,
                'UniswapV2Library: INSUFFICIENT_LIQUIDITY'
            );
            uint256 amountInWithFee = amountIn.mul(997);
            uint256 numerator = amountInWithFee.mul(reserveOut);
            uint256 denominator = reserveIn.mul(1000).add(amountInWithFee);
            amountOut = numerator / denominator;
        }
        // given an output amount of an asset and pair reserves, returns a required input amount of the other asset
        function getAmountIn(
            uint256 amountOut,
            uint256 reserveIn,
            uint256 reserveOut
        ) internal pure returns (uint256 amountIn) {
            require(amountOut > 0, 'UniswapV2Library: INSUFFICIENT_OUTPUT_AMOUNT');
            require(
                reserveIn > 0 && reserveOut > 0,
                'UniswapV2Library: INSUFFICIENT_LIQUIDITY'
            );
            uint256 numerator = reserveIn.mul(amountOut).mul(1000);
            uint256 denominator = reserveOut.sub(amountOut).mul(997);
            amountIn = (numerator / denominator).add(1);
        }
        // performs chained getAmountOut calculations on any number of pairs
        function getAmountsOut(
            address factory,
            uint256 amountIn,
            address[] memory path
        ) internal view returns (uint256[] memory amounts) {
            require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
            amounts = new uint256[](path.length);
            amounts[0] = amountIn;
            for (uint256 i = 0; i < path.length - 1; i++) {
                (uint256 reserveIn, uint256 reserveOut) = getReserves(
                    factory,
                    path[i],
                    path[i + 1]
                );
                amounts[i + 1] = getAmountOut(amounts[i], reserveIn, reserveOut);
            }
        }
        // performs chained getAmountIn calculations on any number of pairs
        function getAmountsIn(
            address factory,
            uint256 amountOut,
            address[] memory path
        ) internal view returns (uint256[] memory amounts) {
            require(path.length >= 2, 'UniswapV2Library: INVALID_PATH');
            amounts = new uint256[](path.length);
            amounts[amounts.length - 1] = amountOut;
            for (uint256 i = path.length - 1; i > 0; i--) {
                (uint256 reserveIn, uint256 reserveOut) = getReserves(
                    factory,
                    path[i - 1],
                    path[i]
                );
                amounts[i - 1] = getAmountIn(amounts[i], reserveIn, reserveOut);
            }
        }
    }
    library UniswapV2OracleLibrary {
        using FixedPoint for *;
        // helper function that returns the current block timestamp within the range of uint32, i.e. [0, 2**32 - 1]
        function currentBlockTimestamp() internal view returns (uint32) {
            return uint32(block.timestamp % 2 ** 32);
        }
        // produces the cumulative price using counterfactuals to save gas and avoid a call to sync.
        function currentCumulativePrices(
            address pair
        )
            internal
            view
            returns (
                uint256 price0Cumulative,
                uint256 price1Cumulative,
                uint32 blockTimestamp
            )
        {
            blockTimestamp = currentBlockTimestamp();
            price0Cumulative = IUniswapV2Pair(pair).price0CumulativeLast();
            price1Cumulative = IUniswapV2Pair(pair).price1CumulativeLast();
            // 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();
            if (blockTimestampLast != blockTimestamp) {
                // subtraction overflow is desired
                uint32 timeElapsed = blockTimestamp - blockTimestampLast;
                // addition overflow is desired
                // counterfactual
                price0Cumulative +=
                    uint256(FixedPoint.fraction(reserve1, reserve0)._x) *
                    timeElapsed;
                // counterfactual
                price1Cumulative +=
                    uint256(FixedPoint.fraction(reserve0, reserve1)._x) *
                    timeElapsed;
            }
        }
    }
    contract UniswapV2OracleV2 is IOracle, Ownable {
        using FixedPoint for *;
        /// @notice oracle that returns price in USD
        IPriceOracleAggregator public immutable aggregator;
        uint256 public PERIOD = 1; // 1 hour TWAP (time-weighted average price)
        uint256 public CONSULT_LENIENCY = 120; // Used for being able to consult past the period end
        bool public ALLOW_STALE_CONSULTS = false; // If false, consult() will fail if the TWAP is stale
        IUniswapV2Pair public immutable pair;
        bool public isFirstToken;
        address public immutable token0;
        address public immutable token1;
        uint256 public price0CumulativeLast;
        uint256 public price1CumulativeLast;
        uint32 public blockTimestampLast;
        FixedPoint.uq112x112 public price0Average;
        FixedPoint.uq112x112 public price1Average;
        constructor(address _prevAggregator) {
            aggregator = UniswapV2Oracle(_prevAggregator).aggregator();
            PERIOD = UniswapV2Oracle(_prevAggregator).PERIOD();
            CONSULT_LENIENCY = UniswapV2Oracle(_prevAggregator).CONSULT_LENIENCY();
            ALLOW_STALE_CONSULTS = UniswapV2Oracle(_prevAggregator)
                .ALLOW_STALE_CONSULTS();
            pair = UniswapV2Oracle(_prevAggregator).pair();
            isFirstToken = UniswapV2Oracle(_prevAggregator).isFirstToken();
            token0 = UniswapV2Oracle(_prevAggregator).token0();
            token1 = UniswapV2Oracle(_prevAggregator).token1();
            price0CumulativeLast = UniswapV2Oracle(_prevAggregator)
                .price0CumulativeLast();
            price1CumulativeLast = UniswapV2Oracle(_prevAggregator)
                .price1CumulativeLast();
            blockTimestampLast = UniswapV2Oracle(_prevAggregator)
                .blockTimestampLast();
            price0Average = FixedPoint.uq112x112(
                UniswapV2Oracle(_prevAggregator).price0Average()
            );
            price1Average = FixedPoint.uq112x112(
                UniswapV2Oracle(_prevAggregator).price1Average()
            );
        }
        function setPeriod(uint256 _period) external onlyOwner {
            PERIOD = _period;
        }
        function setConsultLeniency(uint256 _consult_leniency) external onlyOwner {
            CONSULT_LENIENCY = _consult_leniency;
        }
        function setAllowStaleConsults(
            bool _allow_stale_consults
        ) external onlyOwner {
            ALLOW_STALE_CONSULTS = _allow_stale_consults;
        }
        // Check if update() can be called instead of wasting gas calling it
        function canUpdate() public view returns (bool) {
            uint32 blockTimestamp = UniswapV2OracleLibrary.currentBlockTimestamp();
            uint32 timeElapsed = blockTimestamp - blockTimestampLast; // Overflow is desired
            return (timeElapsed >= PERIOD);
        }
        function update() external {
            (
                uint256 price0Cumulative,
                uint256 price1Cumulative,
                uint32 blockTimestamp
            ) = UniswapV2OracleLibrary.currentCumulativePrices(address(pair));
            // overflow is desired
            uint256 timeElapsed = blockTimestamp > blockTimestampLast
                ? blockTimestamp - blockTimestampLast
                : uint256(blockTimestamp) + 2 ** 32 - uint256(blockTimestampLast);
            // Ensure that at least one full period has passed since the last update
            require(
                timeElapsed >= PERIOD && timeElapsed < 2 ** 32,
                'UniswapPairOracle: PERIOD_NOT_ELAPSED'
            );
            // Overflow is desired, casting never truncates
            // 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)
            );
            price1Average = FixedPoint.uq112x112(
                uint224((price1Cumulative - price1CumulativeLast) / timeElapsed)
            );
            price0CumulativeLast = price0Cumulative;
            price1CumulativeLast = price1Cumulative;
            blockTimestampLast = blockTimestamp;
        }
        /// @dev returns the latest price of asset
        function viewPriceInUSD() external view override returns (uint256 price) {
            uint32 blockTimestamp = UniswapV2OracleLibrary.currentBlockTimestamp();
            uint32 timeElapsed = blockTimestamp - blockTimestampLast; // Overflow is desired
            // Ensure that the price is not stale
            require(
                (timeElapsed < (PERIOD + CONSULT_LENIENCY)) || ALLOW_STALE_CONSULTS,
                'UniswapPairOracle: PRICE_IS_STALE_NEED_TO_CALL_UPDATE'
            );
            if (isFirstToken) {
                price =
                    (aggregator.viewPriceInUSD(token1) *
                        (10 ** IERC20Metadata(token0).decimals())) /
                    (
                        price1Average
                            .mul(10 ** IERC20Metadata(token1).decimals())
                            .decode144()
                    );
            } else {
                price =
                    (aggregator.viewPriceInUSD(token0) *
                        (10 ** IERC20Metadata(token1).decimals())) /
                    (
                        price0Average
                            .mul(10 ** IERC20Metadata(token0).decimals())
                            .decode144()
                    );
            }
        }
    }
    

    File 2 of 2: UniswapV2Pair
    // File: contracts/interfaces/IUniswapV2Pair.sol
    
    pragma solidity >=0.5.0;
    
    interface IUniswapV2Pair {
        event Approval(address indexed owner, address indexed spender, uint value);
        event Transfer(address indexed from, address indexed to, uint value);
    
        function name() external pure returns (string memory);
        function symbol() external pure returns (string memory);
        function decimals() external pure returns (uint8);
        function totalSupply() external view returns (uint);
        function balanceOf(address owner) external view returns (uint);
        function allowance(address owner, address spender) external view returns (uint);
    
        function approve(address spender, uint value) external returns (bool);
        function transfer(address to, uint value) external returns (bool);
        function transferFrom(address from, address to, uint value) external returns (bool);
    
        function DOMAIN_SEPARATOR() external view returns (bytes32);
        function PERMIT_TYPEHASH() external pure returns (bytes32);
        function nonces(address owner) external view returns (uint);
    
        function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
    
        event Mint(address indexed sender, uint amount0, uint amount1);
        event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
        event Swap(
            address indexed sender,
            uint amount0In,
            uint amount1In,
            uint amount0Out,
            uint amount1Out,
            address indexed to
        );
        event Sync(uint112 reserve0, uint112 reserve1);
    
        function MINIMUM_LIQUIDITY() external pure returns (uint);
        function factory() external view returns (address);
        function token0() external view returns (address);
        function token1() external view returns (address);
        function getReserves() external view returns (uint112 reserve0, uint112 reserve1, uint32 blockTimestampLast);
        function price0CumulativeLast() external view returns (uint);
        function price1CumulativeLast() external view returns (uint);
        function kLast() external view returns (uint);
    
        function mint(address to) external returns (uint liquidity);
        function burn(address to) external returns (uint amount0, uint amount1);
        function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external;
        function skim(address to) external;
        function sync() external;
    
        function initialize(address, address) external;
    }
    
    // File: contracts/interfaces/IUniswapV2ERC20.sol
    
    pragma solidity >=0.5.0;
    
    interface IUniswapV2ERC20 {
        event Approval(address indexed owner, address indexed spender, uint value);
        event Transfer(address indexed from, address indexed to, uint value);
    
        function name() external pure returns (string memory);
        function symbol() external pure returns (string memory);
        function decimals() external pure returns (uint8);
        function totalSupply() external view returns (uint);
        function balanceOf(address owner) external view returns (uint);
        function allowance(address owner, address spender) external view returns (uint);
    
        function approve(address spender, uint value) external returns (bool);
        function transfer(address to, uint value) external returns (bool);
        function transferFrom(address from, address to, uint value) external returns (bool);
    
        function DOMAIN_SEPARATOR() external view returns (bytes32);
        function PERMIT_TYPEHASH() external pure returns (bytes32);
        function nonces(address owner) external view returns (uint);
    
        function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external;
    }
    
    // File: contracts/libraries/SafeMath.sol
    
    pragma solidity =0.5.16;
    
    // a library for performing overflow-safe math, courtesy of DappHub (https://github.com/dapphub/ds-math)
    
    library SafeMath {
        function add(uint x, uint y) internal pure returns (uint z) {
            require((z = x + y) >= x, 'ds-math-add-overflow');
        }
    
        function sub(uint x, uint y) internal pure returns (uint z) {
            require((z = x - y) <= x, 'ds-math-sub-underflow');
        }
    
        function mul(uint x, uint y) internal pure returns (uint z) {
            require(y == 0 || (z = x * y) / y == x, 'ds-math-mul-overflow');
        }
    }
    
    // File: contracts/UniswapV2ERC20.sol
    
    pragma solidity =0.5.16;
    
    
    
    contract UniswapV2ERC20 is IUniswapV2ERC20 {
        using SafeMath for uint;
    
        string public constant name = 'Uniswap V2';
        string public constant symbol = 'UNI-V2';
        uint8 public constant decimals = 18;
        uint  public totalSupply;
        mapping(address => uint) public balanceOf;
        mapping(address => mapping(address => uint)) public allowance;
    
        bytes32 public DOMAIN_SEPARATOR;
        // keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");
        bytes32 public constant PERMIT_TYPEHASH = 0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;
        mapping(address => uint) public nonces;
    
        event Approval(address indexed owner, address indexed spender, uint value);
        event Transfer(address indexed from, address indexed to, uint value);
    
        constructor() public {
            uint chainId;
            assembly {
                chainId := chainid
            }
            DOMAIN_SEPARATOR = keccak256(
                abi.encode(
                    keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'),
                    keccak256(bytes(name)),
                    keccak256(bytes('1')),
                    chainId,
                    address(this)
                )
            );
        }
    
        function _mint(address to, uint value) internal {
            totalSupply = totalSupply.add(value);
            balanceOf[to] = balanceOf[to].add(value);
            emit Transfer(address(0), to, value);
        }
    
        function _burn(address from, uint value) internal {
            balanceOf[from] = balanceOf[from].sub(value);
            totalSupply = totalSupply.sub(value);
            emit Transfer(from, address(0), value);
        }
    
        function _approve(address owner, address spender, uint value) private {
            allowance[owner][spender] = value;
            emit Approval(owner, spender, value);
        }
    
        function _transfer(address from, address to, uint value) private {
            balanceOf[from] = balanceOf[from].sub(value);
            balanceOf[to] = balanceOf[to].add(value);
            emit Transfer(from, to, value);
        }
    
        function approve(address spender, uint value) external returns (bool) {
            _approve(msg.sender, spender, value);
            return true;
        }
    
        function transfer(address to, uint value) external returns (bool) {
            _transfer(msg.sender, to, value);
            return true;
        }
    
        function transferFrom(address from, address to, uint value) external returns (bool) {
            if (allowance[from][msg.sender] != uint(-1)) {
                allowance[from][msg.sender] = allowance[from][msg.sender].sub(value);
            }
            _transfer(from, to, value);
            return true;
        }
    
        function permit(address owner, address spender, uint value, uint deadline, uint8 v, bytes32 r, bytes32 s) external {
            require(deadline >= block.timestamp, 'UniswapV2: EXPIRED');
            bytes32 digest = keccak256(
                abi.encodePacked(
                    '\x19\x01',
                    DOMAIN_SEPARATOR,
                    keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline))
                )
            );
            address recoveredAddress = ecrecover(digest, v, r, s);
            require(recoveredAddress != address(0) && recoveredAddress == owner, 'UniswapV2: INVALID_SIGNATURE');
            _approve(owner, spender, value);
        }
    }
    
    // File: contracts/libraries/Math.sol
    
    pragma solidity =0.5.16;
    
    // a library for performing various math operations
    
    library Math {
        function min(uint x, uint y) internal pure returns (uint z) {
            z = x < y ? x : y;
        }
    
        // babylonian method (https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method)
        function sqrt(uint y) internal pure returns (uint z) {
            if (y > 3) {
                z = y;
                uint x = y / 2 + 1;
                while (x < z) {
                    z = x;
                    x = (y / x + x) / 2;
                }
            } else if (y != 0) {
                z = 1;
            }
        }
    }
    
    // File: contracts/libraries/UQ112x112.sol
    
    pragma solidity =0.5.16;
    
    // a library for handling binary fixed point numbers (https://en.wikipedia.org/wiki/Q_(number_format))
    
    // range: [0, 2**112 - 1]
    // resolution: 1 / 2**112
    
    library UQ112x112 {
        uint224 constant Q112 = 2**112;
    
        // encode a uint112 as a UQ112x112
        function encode(uint112 y) internal pure returns (uint224 z) {
            z = uint224(y) * Q112; // never overflows
        }
    
        // divide a UQ112x112 by a uint112, returning a UQ112x112
        function uqdiv(uint224 x, uint112 y) internal pure returns (uint224 z) {
            z = x / uint224(y);
        }
    }
    
    // File: contracts/interfaces/IERC20.sol
    
    pragma solidity >=0.5.0;
    
    interface IERC20 {
        event Approval(address indexed owner, address indexed spender, uint value);
        event Transfer(address indexed from, address indexed to, uint value);
    
        function name() external view returns (string memory);
        function symbol() external view returns (string memory);
        function decimals() external view returns (uint8);
        function totalSupply() external view returns (uint);
        function balanceOf(address owner) external view returns (uint);
        function allowance(address owner, address spender) external view returns (uint);
    
        function approve(address spender, uint value) external returns (bool);
        function transfer(address to, uint value) external returns (bool);
        function transferFrom(address from, address to, uint value) external returns (bool);
    }
    
    // File: contracts/interfaces/IUniswapV2Factory.sol
    
    pragma solidity >=0.5.0;
    
    interface IUniswapV2Factory {
        event PairCreated(address indexed token0, address indexed token1, address pair, uint);
    
        function feeTo() external view returns (address);
        function feeToSetter() external view returns (address);
    
        function getPair(address tokenA, address tokenB) external view returns (address pair);
        function allPairs(uint) external view returns (address pair);
        function allPairsLength() external view returns (uint);
    
        function createPair(address tokenA, address tokenB) external returns (address pair);
    
        function setFeeTo(address) external;
        function setFeeToSetter(address) external;
    }
    
    // File: contracts/interfaces/IUniswapV2Callee.sol
    
    pragma solidity >=0.5.0;
    
    interface IUniswapV2Callee {
        function uniswapV2Call(address sender, uint amount0, uint amount1, bytes calldata data) external;
    }
    
    // File: contracts/UniswapV2Pair.sol
    
    pragma solidity =0.5.16;
    
    
    
    
    
    
    
    
    contract UniswapV2Pair is IUniswapV2Pair, UniswapV2ERC20 {
        using SafeMath  for uint;
        using UQ112x112 for uint224;
    
        uint public constant MINIMUM_LIQUIDITY = 10**3;
        bytes4 private constant SELECTOR = bytes4(keccak256(bytes('transfer(address,uint256)')));
    
        address public factory;
        address public token0;
        address public token1;
    
        uint112 private reserve0;           // uses single storage slot, accessible via getReserves
        uint112 private reserve1;           // uses single storage slot, accessible via getReserves
        uint32  private blockTimestampLast; // uses single storage slot, accessible via getReserves
    
        uint public price0CumulativeLast;
        uint public price1CumulativeLast;
        uint public kLast; // reserve0 * reserve1, as of immediately after the most recent liquidity event
    
        uint private unlocked = 1;
        modifier lock() {
            require(unlocked == 1, 'UniswapV2: LOCKED');
            unlocked = 0;
            _;
            unlocked = 1;
        }
    
        function getReserves() public view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast) {
            _reserve0 = reserve0;
            _reserve1 = reserve1;
            _blockTimestampLast = blockTimestampLast;
        }
    
        function _safeTransfer(address token, address to, uint value) private {
            (bool success, bytes memory data) = token.call(abi.encodeWithSelector(SELECTOR, to, value));
            require(success && (data.length == 0 || abi.decode(data, (bool))), 'UniswapV2: TRANSFER_FAILED');
        }
    
        event Mint(address indexed sender, uint amount0, uint amount1);
        event Burn(address indexed sender, uint amount0, uint amount1, address indexed to);
        event Swap(
            address indexed sender,
            uint amount0In,
            uint amount1In,
            uint amount0Out,
            uint amount1Out,
            address indexed to
        );
        event Sync(uint112 reserve0, uint112 reserve1);
    
        constructor() public {
            factory = msg.sender;
        }
    
        // called once by the factory at time of deployment
        function initialize(address _token0, address _token1) external {
            require(msg.sender == factory, 'UniswapV2: FORBIDDEN'); // sufficient check
            token0 = _token0;
            token1 = _token1;
        }
    
        // update reserves and, on the first call per block, price accumulators
        function _update(uint balance0, uint balance1, uint112 _reserve0, uint112 _reserve1) private {
            require(balance0 <= uint112(-1) && balance1 <= uint112(-1), 'UniswapV2: OVERFLOW');
            uint32 blockTimestamp = uint32(block.timestamp % 2**32);
            uint32 timeElapsed = blockTimestamp - blockTimestampLast; // overflow is desired
            if (timeElapsed > 0 && _reserve0 != 0 && _reserve1 != 0) {
                // * never overflows, and + overflow is desired
                price0CumulativeLast += uint(UQ112x112.encode(_reserve1).uqdiv(_reserve0)) * timeElapsed;
                price1CumulativeLast += uint(UQ112x112.encode(_reserve0).uqdiv(_reserve1)) * timeElapsed;
            }
            reserve0 = uint112(balance0);
            reserve1 = uint112(balance1);
            blockTimestampLast = blockTimestamp;
            emit Sync(reserve0, reserve1);
        }
    
        // if fee is on, mint liquidity equivalent to 1/6th of the growth in sqrt(k)
        function _mintFee(uint112 _reserve0, uint112 _reserve1) private returns (bool feeOn) {
            address feeTo = IUniswapV2Factory(factory).feeTo();
            feeOn = feeTo != address(0);
            uint _kLast = kLast; // gas savings
            if (feeOn) {
                if (_kLast != 0) {
                    uint rootK = Math.sqrt(uint(_reserve0).mul(_reserve1));
                    uint rootKLast = Math.sqrt(_kLast);
                    if (rootK > rootKLast) {
                        uint numerator = totalSupply.mul(rootK.sub(rootKLast));
                        uint denominator = rootK.mul(5).add(rootKLast);
                        uint liquidity = numerator / denominator;
                        if (liquidity > 0) _mint(feeTo, liquidity);
                    }
                }
            } else if (_kLast != 0) {
                kLast = 0;
            }
        }
    
        // this low-level function should be called from a contract which performs important safety checks
        function mint(address to) external lock returns (uint liquidity) {
            (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
            uint balance0 = IERC20(token0).balanceOf(address(this));
            uint balance1 = IERC20(token1).balanceOf(address(this));
            uint amount0 = balance0.sub(_reserve0);
            uint amount1 = balance1.sub(_reserve1);
    
            bool feeOn = _mintFee(_reserve0, _reserve1);
            uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
            if (_totalSupply == 0) {
                liquidity = Math.sqrt(amount0.mul(amount1)).sub(MINIMUM_LIQUIDITY);
               _mint(address(0), MINIMUM_LIQUIDITY); // permanently lock the first MINIMUM_LIQUIDITY tokens
            } else {
                liquidity = Math.min(amount0.mul(_totalSupply) / _reserve0, amount1.mul(_totalSupply) / _reserve1);
            }
            require(liquidity > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_MINTED');
            _mint(to, liquidity);
    
            _update(balance0, balance1, _reserve0, _reserve1);
            if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
            emit Mint(msg.sender, amount0, amount1);
        }
    
        // this low-level function should be called from a contract which performs important safety checks
        function burn(address to) external lock returns (uint amount0, uint amount1) {
            (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
            address _token0 = token0;                                // gas savings
            address _token1 = token1;                                // gas savings
            uint balance0 = IERC20(_token0).balanceOf(address(this));
            uint balance1 = IERC20(_token1).balanceOf(address(this));
            uint liquidity = balanceOf[address(this)];
    
            bool feeOn = _mintFee(_reserve0, _reserve1);
            uint _totalSupply = totalSupply; // gas savings, must be defined here since totalSupply can update in _mintFee
            amount0 = liquidity.mul(balance0) / _totalSupply; // using balances ensures pro-rata distribution
            amount1 = liquidity.mul(balance1) / _totalSupply; // using balances ensures pro-rata distribution
            require(amount0 > 0 && amount1 > 0, 'UniswapV2: INSUFFICIENT_LIQUIDITY_BURNED');
            _burn(address(this), liquidity);
            _safeTransfer(_token0, to, amount0);
            _safeTransfer(_token1, to, amount1);
            balance0 = IERC20(_token0).balanceOf(address(this));
            balance1 = IERC20(_token1).balanceOf(address(this));
    
            _update(balance0, balance1, _reserve0, _reserve1);
            if (feeOn) kLast = uint(reserve0).mul(reserve1); // reserve0 and reserve1 are up-to-date
            emit Burn(msg.sender, amount0, amount1, to);
        }
    
        // this low-level function should be called from a contract which performs important safety checks
        function swap(uint amount0Out, uint amount1Out, address to, bytes calldata data) external lock {
            require(amount0Out > 0 || amount1Out > 0, 'UniswapV2: INSUFFICIENT_OUTPUT_AMOUNT');
            (uint112 _reserve0, uint112 _reserve1,) = getReserves(); // gas savings
            require(amount0Out < _reserve0 && amount1Out < _reserve1, 'UniswapV2: INSUFFICIENT_LIQUIDITY');
    
            uint balance0;
            uint balance1;
            { // scope for _token{0,1}, avoids stack too deep errors
            address _token0 = token0;
            address _token1 = token1;
            require(to != _token0 && to != _token1, 'UniswapV2: INVALID_TO');
            if (amount0Out > 0) _safeTransfer(_token0, to, amount0Out); // optimistically transfer tokens
            if (amount1Out > 0) _safeTransfer(_token1, to, amount1Out); // optimistically transfer tokens
            if (data.length > 0) IUniswapV2Callee(to).uniswapV2Call(msg.sender, amount0Out, amount1Out, data);
            balance0 = IERC20(_token0).balanceOf(address(this));
            balance1 = IERC20(_token1).balanceOf(address(this));
            }
            uint amount0In = balance0 > _reserve0 - amount0Out ? balance0 - (_reserve0 - amount0Out) : 0;
            uint amount1In = balance1 > _reserve1 - amount1Out ? balance1 - (_reserve1 - amount1Out) : 0;
            require(amount0In > 0 || amount1In > 0, 'UniswapV2: INSUFFICIENT_INPUT_AMOUNT');
            { // scope for reserve{0,1}Adjusted, avoids stack too deep errors
            uint balance0Adjusted = balance0.mul(1000).sub(amount0In.mul(3));
            uint balance1Adjusted = balance1.mul(1000).sub(amount1In.mul(3));
            require(balance0Adjusted.mul(balance1Adjusted) >= uint(_reserve0).mul(_reserve1).mul(1000**2), 'UniswapV2: K');
            }
    
            _update(balance0, balance1, _reserve0, _reserve1);
            emit Swap(msg.sender, amount0In, amount1In, amount0Out, amount1Out, to);
        }
    
        // force balances to match reserves
        function skim(address to) external lock {
            address _token0 = token0; // gas savings
            address _token1 = token1; // gas savings
            _safeTransfer(_token0, to, IERC20(_token0).balanceOf(address(this)).sub(reserve0));
            _safeTransfer(_token1, to, IERC20(_token1).balanceOf(address(this)).sub(reserve1));
        }
    
        // force reserves to match balances
        function sync() external lock {
            _update(IERC20(token0).balanceOf(address(this)), IERC20(token1).balanceOf(address(this)), reserve0, reserve1);
        }
    }